Loading

规约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); } }

 

posted @ 2021-01-18 11:48  奔跑石头  阅读(363)  评论(0)    收藏  举报