C#如何规范实现请求与实体的匹配过滤逻辑
当前实现的问题
现有写法主要存在两个不规范的点:
- 职责越界:
Gym作为数据/领域实体,不应该感知应用层的GetGymRequest请求结构,IsAppreciateToRequest这类和业务查询强绑定的逻辑放在实体类里,会导致领域层和应用层耦合,后续实体复用、请求结构变更时维护成本极高 - 逻辑零散:过滤规则没有统一的抽象层,要么散落在Manager方法里堆冗长判断,要么散落在各个实体类里,无法复用,每次新增查询接口都要重复写类似的条件判断,冗余严重
标准重构实现
核心思路是用规约模式统一封装所有过滤规则,把过滤逻辑从实体类、Manager层抽离到独立的规约层,各层职责清晰边界明确。
1. 定义通用规约抽象
先定义所有过滤规则统一实现的接口,强制约束规约的输出形式:
// 通用规约接口,所有实体的过滤规则都实现该接口 public interface ISpecification<T> { Expression<Func<T, bool>> FilterCondition { get; } }
2. 针对查询场景实现具体规约
把原来写在IsAppreciateToRequest里的判断逻辑,挪到专门针对Gym查询的规约类中,后续所有Gym相关的过滤规则都只在这个类里维护:
public class GymQuerySpecification : ISpecification<Gym> { private readonly GetGymRequest _request; public GymQuerySpecification(GetGymRequest request) { _request = request; } public Expression<Func<Gym, bool>> FilterCondition { get { return gym => (string.IsNullOrEmpty(_request.Name) || gym.Name == _request.Name) && (string.IsNullOrEmpty(_request.Location) || gym.Location == _request.Location) && (_request.SectionRequest == null || gym.Sections.All(section => // Section维度的过滤逻辑直接在这里维护,不需要给Section实体也加请求耦合的判断方法 (string.IsNullOrEmpty(_request.SectionRequest.Name) || section.Name == _request.SectionRequest.Name) && (!_request.SectionRequest.MinCapacity.HasValue || section.Capacity >= _request.SectionRequest.MinCapacity.Value) )); } } }
3. 改造仓储层适配规约
给Repository的Find方法增加规约入参的重载,统一处理查询执行逻辑:
public IEnumerable<Gym> Find(ISpecification<Gym> specification) { return _dbContext.Gyms .Where(specification.FilterCondition) .ToList(); }
4. 简化Manager层逻辑
Manager层不再承载任何过滤判断逻辑,只做流程编排:接收请求、组装对应规约、调用仓储、转换DTO返回,代码结构固定,不会再出现几十行冗余过滤代码的情况:
public IEnumerable<GymDto> GetGyms(GetGymRequest request) { var querySpec = new GymQuerySpecification(request); return _gymRepository .Find(querySpec) .Select(GymDto.FromEntityToDto); }
重构收益
- 职责边界清晰:领域实体不再依赖上层请求DTO,Manager层只做流程编排,过滤逻辑统一收敛在规约层
- 代码复用性高:相同过滤规则的规约可以在不同业务场景直接复用,不需要重复写条件判断
- 维护成本低:后续调整过滤规则只需要修改对应规约类,不需要翻找Manager或者实体类的零散代码,也不会出现改漏逻辑的问题
内容的提问来源于stack exchange,提问作者Red Star
相关产品推荐
相关产品推荐

