规约Specification模式

  先从下面的一个示例引入我们要介绍的内容。User没有什么特殊,一个普通的领域对象,它提供了一些查询方法。

复制代码
public class User 
{  
    public string ID { get; set; }  
    public string FirstName { get; set; }  
    public string LastName { get; set; }  
    public string Department { get; set; }

public int Age { get; set; }
  public List<User> GetUserName(string name)    {}   

publicList
<User> GetUserDepartment(string department)    {}
}
复制代码

  在上面的例子中,有什么问题呢,如果需要添加新的过滤条件,按不同的属性做条件进行过滤。会想到怎样处理呢?

增加一些新的查询方法?或是修改当前的方法?

 

我们来介绍下规约模式

    將一些领域知识封装到一个同一单元 --规约--,提升 可重用,可维护,和松耦合的能力。通过bool逻辑將业务规则链接在一起,实现重组业务规则。

 定义规约 

 public interface ISpecification<T> 
    {
        public bool IsSatisfiedBy(T u);
    }

 

复制代码
public class UserDepartmentSpecification : ISpecification<User>
    {
        private readonly string _department;

        public EmployeeDepartmentSpecification(string depatrment)
        {
            _department = depatrment;
        }

        public bool IsSatisfiedBy(User employee)
        {
            return employee.Department.Equals(_department);
        }
    }

    public class UserAgeSpecification : ISpecification<User>
    {
        private readonly int _age;

        public EmployeeYearSpecification(int age)
        {
            _age= age;
        }
        public bool IsSatisfiedBy(User employee)
        {
            return employee.Aga == _age;
        }
    }
复制代码

  Service:

复制代码
public class UserService
    {
        public static List<User> GetUserBy(ISpecification<User> specification, List<User> user)
        {
            List<User> NeededUsers= new List<User>();


            foreach (var u in user)
            {
                if (specification.IsSatisfiedBy(u))
                {
                    NeededUsers.Add(u);
                }
            }
            return NeededUsers ;
 } }
复制代码

  通过规约模式,我们移除了大量最对性的查询方法。如果还需要针对其他属性的过滤条件,也只需要新增新的规约。保持了模型的稳定。

使用 LINQ类别(Orm)的仓储查询

  这里我们拿ESHOPWeb 的规约实现学习,核心接口的定义如下图。

 

ISpecification

复制代码
public interface ISpecification<T, TResult> : ISpecification<T>
    {
        Expression<Func<T, TResult>>? Selector { get; }
    }

    public interface ISpecification<T>
    {
        IEnumerable<Expression<Func<T, bool>>> WhereExpressions { get; }
        IEnumerable<(Expression<Func<T, object>> KeySelector, OrderTypeEnum OrderType)> OrderExpressions { get; }
        IEnumerable<IIncludeAggregator> IncludeAggregators { get; }
        IEnumerable<string> IncludeStrings { get; }
        IEnumerable<(Expression<Func<T, string>> Selector, string SearchTerm, int SearchGroup)> SearchCriterias { get; }

        int? Take { get; }
        int? Skip { get; }
        [Obsolete]
        bool IsPagingEnabled { get; }

        bool CacheEnabled { get; }
        string? CacheKey { get; }
    }

 public abstract class Specification<T, TResult> : Specification<T>, ISpecification<T, TResult>
    {
        protected new virtual ISpecificationBuilder<T, TResult> Query { get; }

        protected Specification() : base()
        {
            this.Query = new SpecificationBuilder<T, TResult>(this);
        }

        public Expression<Func<T, TResult>>? Selector { get; internal set; }
    }

    public abstract class Specification<T> : ISpecification<T>
    {
        protected virtual ISpecificationBuilder<T> Query { get; }

        protected Specification()
        {
            this.Query = new SpecificationBuilder<T>(this);
        }

        public IEnumerable<Expression<Func<T, bool>>> WhereExpressions { get; } = new List<Expression<Func<T, bool>>>();

        public IEnumerable<(Expression<Func<T, object>> KeySelector, OrderTypeEnum OrderType)> OrderExpressions { get; } = 
            new List<(Expression<Func<T, object>> KeySelector, OrderTypeEnum OrderType)>();

        public IEnumerable<IIncludeAggregator> IncludeAggregators { get; } = new List<IIncludeAggregator>();

        public IEnumerable<string> IncludeStrings { get; } = new List<string>();

        public IEnumerable<(Expression<Func<T, string>> Selector, string SearchTerm, int SearchGroup)> SearchCriterias { get; } =
            new List<(Expression<Func<T, string>> Selector, string SearchTerm, int SearchGroup)>();


        public int? Take { get; internal set; } = null;

        public int? Skip { get; internal set; } = null;

        public bool IsPagingEnabled { get; internal set; } = false;

        public string? CacheKey { get; internal set; }

        public bool CacheEnabled { get; internal set; }
    }
复制代码

SpecificationBuilder

复制代码
public interface ISpecificationBuilder<T, TResult> : ISpecificationBuilder<T>
{
new Specification<T, TResult> Specification { get; }
}

public interface ISpecificationBuilder<T>
{
Specification<T> Specification { get; }
}
  

 public class SpecificationBuilder<T, TResult> : SpecificationBuilder<T>, ISpecificationBuilder<T, TResult>
    {
        public new Specification<T, TResult> Specification { get; }

        public SpecificationBuilder(Specification<T, TResult> specification) 
            : base(specification)
        {
            this.Specification = specification;
        }
    }

    public class SpecificationBuilder<T> : ISpecificationBuilder<T>
    {
        public Specification<T> Specification { get; }

        public SpecificationBuilder(Specification<T> specification)
        {
            this.Specification = specification;
        }
    } 
复制代码

ISpecificationEvaluator 会根据规约生成对应的查询表达式

实现与调用

   自定义规约的时候实现ISpecification,通过 Query属性( ISpecificationBuilder),构建查询。

复制代码
  public class BlogWithPostsAndAuthorSpec : Specification<Blog>
    {
        public BlogWithPostsAndAuthorSpec(int id)
        {
            Query.Where(b => b.Id == id)
                .Include(b => b.Posts)
                .ThenInclude(p => p.Author);
        }
    }
复制代码

 在仓储里,通过 ISpecificationEvaluator<T>  处理查询表达式,返回IQueryable<T>

复制代码
 public abstract class RepositoryBase<T> : IRepositoryBase<T> where T : class
    {
        ......
     ......

private readonly ISpecificationEvaluator<T> specificationEvaluator;

public async Task<List<T>> ListAsync(ISpecification<T> specification) { return await ApplySpecification(specification).ToListAsync(); } public async Task<List<TResult>> ListAsync<TResult>(ISpecification<T, TResult> specification) { return await ApplySpecification(specification).ToListAsync(); } public async Task<int> CountAsync(ISpecification<T> specification) { return await ApplySpecification(specification).CountAsync(); } protected IQueryable<T> ApplySpecification(ISpecification<T> specification) { return specificationEvaluator.GetQuery(dbContext.Set<T>().AsQueryable(), specification); } protected IQueryable<TResult> ApplySpecification<TResult>(ISpecification<T, TResult> specification) { if (specification is null) throw new ArgumentNullException("Specification is required"); if (specification.Selector is null) throw new SelectorNotFoundException(); return specificationEvaluator.GetQuery(dbContext.Set<T>().AsQueryable(), specification); } }
复制代码

 

作者:RunStone

出处:https://www.cnblogs.com/RunStone/p/14285023.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   奔跑石头  阅读(341)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
more_horiz
keyboard_arrow_up light_mode palette
选择主题
点击右上角即可分享
微信分享提示