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

浙公网安备 33010602011771号