规约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 ListGetUserName(string name) {}
publicListGetUserDepartment(string department) {}
}
在上面的例子中,有什么问题呢,如果需要添加新的过滤条件,按不同的属性做条件进行过滤。会想到怎样处理呢?
怎加一些新的查询方法?或是修改当前的方法?
下面介绍下规约模式
將一些领域知识封装到一个同一单元 --规约--,提升 可重用,可维护,和松耦合的能力。通过bool逻辑將业务规则链接在一起,实现重组业务规则。
定义规约
public interface ISpecification{ public bool IsSatisfiedBy(T u); }
public class UserDepartmentSpecification : ISpecification{ private readonly string _department; public EmployeeDepartmentSpecification(string depatrment) { _department = depatrment; } public bool IsSatisfiedBy(User employee) { return employee.Department.Equals(_department); } } public class UserAgeSpecification : ISpecification { 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 ListGetUserBy(ISpecification specification, List user) { List NeededUsers= new List (); foreach (var u in user) { if (specification.IsSatisfiedBy(u)) { NeededUsers.Add(u); } } return NeededUsers ; } }
通过规约模式,我们移除了大量最对性的查询方法。如果还需要针对其他属性的过滤条件,也只需要新增新的规约。保持了模型的稳定。
使用 LINQ类别(Orm)的仓储查询
这里我们拿ESHOPWeb 的规约实现学习,核心接口的定义如下图。
ISpecification
public interface ISpecification: ISpecification { Expression >? Selector { get; } } public interface ISpecification { IEnumerable bool>>> WhereExpressions { get; } IEnumerable<(Expression object>> KeySelector, OrderTypeEnum OrderType)> OrderExpressions { get; } IEnumerable IncludeAggregators { get; } IEnumerable<string> IncludeStrings { get; } IEnumerable<(Expression 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 : Specification , ISpecification { protected new virtual ISpecificationBuilder Query { get; } protected Specification() : base() { this.Query = new SpecificationBuilder (this); } public Expression >? Selector { get; internal set; } } public abstract class Specification : ISpecification { protected virtual ISpecificationBuilder Query { get; } protected Specification() { this.Query = new SpecificationBuilder (this); } public IEnumerable bool>>> WhereExpressions { get; } = new List bool>>>(); public IEnumerable<(Expression object>> KeySelector, OrderTypeEnum OrderType)> OrderExpressions { get; } = new List<(Expression object>> KeySelector, OrderTypeEnum OrderType)>(); public IEnumerable IncludeAggregators { get; } = new List (); public IEnumerable<string> IncludeStrings { get; } = new List<string>(); public IEnumerable<(Expression string>> Selector, string SearchTerm, int SearchGroup)> SearchCriterias { get; } = new List<(Expression 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: ISpecificationBuilder { new Specification Specification { get; } } public interface ISpecificationBuilder { Specification Specification { get; } } public class SpecificationBuilder : SpecificationBuilder , ISpecificationBuilder { public new Specification Specification { get; } public SpecificationBuilder(Specification specification) : base(specification) { this.Specification = specification; } } public class SpecificationBuilder : ISpecificationBuilder { public Specification Specification { get; } public SpecificationBuilder(Specification specification) { this.Specification = specification; } }
ISpecificationEvaluator 会根据规约生成对应的查询表达式
实现与调用
自定义规约的时候实现ISpecification,通过 Query属性( ISpecificationBuilder),构建查询。
public class BlogWithPostsAndAuthorSpec : Specification{ public BlogWithPostsAndAuthorSpec(int id) { Query.Where(b => b.Id == id) .Include(b => b.Posts) .ThenInclude(p => p.Author); } }
在仓储里,通过 ISpecificationEvaluator
public abstract class RepositoryBase: IRepositoryBase where T : class { ......
......
private readonly ISpecificationEvaluatorspecificationEvaluator;
public async Task> ListAsync(ISpecification
specification) { return await ApplySpecification(specification).ToListAsync(); } public async Task > ListAsync
(ISpecification specification) { return await ApplySpecification(specification).ToListAsync(); } public async Task<int> CountAsync(ISpecification specification) { return await ApplySpecification(specification).CountAsync(); } protected IQueryable ApplySpecification(ISpecification specification) { return specificationEvaluator.GetQuery(dbContext.Set ().AsQueryable(), specification); } protected IQueryable ApplySpecification (ISpecification specification) { if (specification is null) throw new ArgumentNullException("Specification is required"); if (specification.Selector is null) throw new SelectorNotFoundException(); return specificationEvaluator.GetQuery(dbContext.Set ().AsQueryable(), specification); } }