# 从一段代码说起 通常在`Application`层的`IRequestHandler`中涉及到查询、排序、分页会有一大段代码。 ``` public class SomeRequest : IRequest> { //是否加载一对多关系 public bool LoadChildren {get;set;} public bool IsPagingEnabled{get;set;} public int Page{get;set;} public int PageSize{get;set;} ... } public class SomeRequestDto { } public class SomeRequestHandler : IRequestHandler> { private readonly IBreakerRepo _breakerRepo; public SomeRequestHandler(IBreakerRepo breakerRepo) { _breakerRepo = breakerRepo; } public asyn Task> Handle(SomeRequest request, CancellationTolen canellationTolen) { var breakers = _breakerRepo.GetAll(); breakers = breakers.OrderBy(t=>t.Name); if(request.LoadChildren) { breakers = breakers.Include(t => t.BreakerData); } if(request.IsPagingEnablled) { breakers = breakers.Skip(request.Page).Take(request.PageSize); } if(!string.IsNullOrEmpty(request.Name)) { breakers = breakers.Where(t=>t.Name.Contains(request.Name)); } ...... } } ``` > 是否可以把这部分逻辑放到基础设施层、或者领域层呢?然后通过调用`var breakers =await _breakerRepo.ListAsync(request)`替换以上逻辑?在`DDD`中,有一种`Specification Patter`模式特别适合这种场景,可以把相关逻辑封装在领域层,这样根据模块化,也更方便单元测试。 ``` //在领域层定义Specification public class CustomerSpec : Specification { public CustomerSpec(CustomerFilter filter) { ... } } //应用 var spec = new CustomerSpec(); var breakers = await _breakerRepo.ListAsync(spec); ``` `Ardalis.Specification`遵循了`Specification Pattern`,很好地解决了上述问题。 ``` ``` # 领域层 一对多关系 ``` public class Customer : IAggregateRoot { public int Id { get; private set; } public string Name { get; private set; } public string Email { get; private set; } public string Address { get; private set; } public IEnumerable Stores => _stores.AsEnumerable(); private readonly List _stores = new List(); public Customer(string name, string email, string address) { Guard.Against.NullOrEmpty(name, nameof(name)); Guard.Against.NullOrEmpty(email, nameof(email)); this.Name = name; this.Email = email; this.Address = address; } ...... } public class Store { public int Id { get; private set; } public string Name { get; private set; } public string Address { get; private set; } public int CustomerId { get; private set; } public Store(string name, string address) { Guard.Against.NullOrEmpty(name, nameof(name)); this.Name = name; this.Address = address; } } ``` 查询条件 ``` public class BaseFilter { public bool LoadChildren { get; set; } public bool IsPagingEnabled { get; set; } public int Page { get; set; } public int PageSize { get; set; } } public class CustomerFilter : BaseFilter { public string Name { get; set; } public string Email { get; set; } public string Address { get; set; } } ``` 定义`Specification`相关 ``` namespace Sample.Core.Specifications { public class CustomerSpec : Specification { public CustomerSpec(CustomerFilter filter) { Query.OrderBy(t => t.Name) .ThenByDescending(t => t.Address); if (filter.LoadChildren) Query.Include(x => x.Stores); if (filter.IsPagingEnabled) Query.Skip(PaginationHelper.CalculateSkip(filter)) .Take(PaginationHelper.CalculateTake(filter)); if (!string.IsNullOrEmpty(filter.Name)) Query.Where(t => t.Name == filter.Name); if (!string.IsNullOrEmpty(filter.Email)) Query.Where(x => x.Email == filter.Email); if (!string.IsNullOrEmpty(filter.Address)) Query.Search(x => x.Address, "%" + filter.Address + "%"); } } } ``` 实现`Ardalis.Specification`的一个接口 ``` using Ardalis.Specification; namespace Sample.Core.Interfaces { public interface IRepository : IRepositoryBase where T : class, IAggregateRoot { } } ``` # 基础设施层 `IRepository`的实现 ``` using Ardalis.Specification.EntityFrameworkCore; namespace Sample.Infra.Data { public class MyRepository : RepositoryBase, IRepository where T : class, IAggregateRoot { private readonly SampleDbContext dbContext; public MyRepository(SampleDbContext dbContext) : base(dbContext) { this.dbContext = dbContext; } // Not required to implement anything. Add additional functionalities if required. } } ``` 注册 ``` services.AddScoped(typeof(MyRepository<>)); ``` # `Application`层或`UI`层调用 ``` private readonly IRepository customerRepository; var spec = new CustomerSpec(); var customers = await customerRepository.ListAsync(spec); ``` 源码位置:`F:\demos\CleanArchitecture\UseSpecification`