ASP.NET Core CQRS架构下避免多参数请求实现Person重复校验方案咨询
在CQRS模式下无唯一键场景的重复校验方案
针对你在ASP.NET Core+MediatR实现CQRS时遇到的问题,这里提供几个符合清洁架构原则的解决方案,避免请求类携带多个零散参数:
1. 用值对象封装校验字段
将FirstName、LastName、Nickname封装成一个不可变值对象,作为请求的单一参数,既符合领域建模思想,也让请求类保持简洁。
代码示例:
首先定义值对象:
namespace HIM_WebApp.Domain.ValueObjects { public record PersonFullIdentity(string FirstName, string LastName, string Nickname) { // 可添加字段合法性校验逻辑,比如非空校验 public static PersonFullIdentity Create(string firstName, string lastName, string nickname) { if (string.IsNullOrWhiteSpace(firstName)) throw new ArgumentException("First name cannot be empty"); // 其他字段校验... return new PersonFullIdentity(firstName, lastName, nickname); } } }
然后修改请求类:
namespace HIM_WebApp.Application.Features.Persons.Requests.Queries { public class CheckRepeatedPersonRequest : IRequest<bool> { public PersonFullIdentity Identity { get; set; } } }
处理器中直接使用值对象的属性进行查询校验:
public class CheckRepeatedPersonHandler : IRequestHandler<CheckRepeatedPersonRequest, bool> { private readonly IRepository<Person> _personRepo; public CheckRepeatedPersonHandler(IRepository<Person> personRepo) { _personRepo = personRepo; } public async Task<bool> Handle(CheckRepeatedPersonRequest request, CancellationToken cancellationToken) { var identity = request.Identity; return await _personRepo.AnyAsync(p => p.FirstName == identity.FirstName && p.LastName == identity.LastName && p.Nickname == identity.Nickname, cancellationToken); } }
2. 在创建命令的管道中嵌入校验
如果重复校验是新增Person操作的前置步骤,无需单独发起查询请求,可通过**MediatR管道行为(Pipeline Behavior)**拦截CreatePersonCommand,自动完成重复校验,避免冗余请求。
代码示例:
首先定义创建命令:
public class CreatePersonCommand : IRequest<Guid> { public string FirstName { get; set; } public string LastName { get; set; } public string Nickname { get; set; } // 其他字段:Age, Gender, Image... }
然后实现管道行为:
public class PersonDuplicateCheckBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : CreatePersonCommand { private readonly IRepository<Person> _personRepo; public PersonDuplicateCheckBehavior(IRepository<Person> personRepo) { _personRepo = personRepo; } public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken) { // 执行重复校验 var isDuplicate = await _personRepo.AnyAsync(p => p.FirstName == request.FirstName && p.LastName == request.LastName && p.Nickname == request.Nickname, cancellationToken); if (isDuplicate) throw new InvalidOperationException("该身份信息已存在"); // 校验通过,继续执行后续命令处理器 return await next(); } }
最后在DI容器中注册管道行为:
services.AddTransient(typeof(IPipelineBehavior<CreatePersonCommand, Guid>), typeof(PersonDuplicateCheckBehavior<CreatePersonCommand, Guid>));
3. 领域层封装校验逻辑
将重复校验逻辑下沉到领域层,通过领域服务或模型静态方法实现,让应用层仅调用领域层接口,符合清洁架构的依赖规则(依赖方向从外向内)。
代码示例:
定义领域服务接口:
namespace HIM_WebApp.Domain.Services { public interface IPersonDomainService { Task<bool> IsIdentityDuplicateAsync(string firstName, string lastName, string nickname, CancellationToken cancellationToken); } }
实现领域服务:
public class PersonDomainService : IPersonDomainService { private readonly IRepository<Person> _personRepo; public PersonDomainService(IRepository<Person> personRepo) { _personRepo = personRepo; } public async Task<bool> IsIdentityDuplicateAsync(string firstName, string lastName, string nickname, CancellationToken cancellationToken) { return await _personRepo.AnyAsync(p => p.FirstName == firstName && p.LastName == lastName && p.Nickname == nickname, cancellationToken); } }
应用层请求处理器直接调用领域服务:
public class CheckRepeatedPersonHandler : IRequestHandler<CheckRepeatedPersonRequest, bool> { private readonly IPersonDomainService _personDomainService; public CheckRepeatedPersonHandler(IPersonDomainService personDomainService) { _personDomainService = personDomainService; } public async Task<bool> Handle(CheckRepeatedPersonRequest request, CancellationToken cancellationToken) { return await _personDomainService.IsIdentityDuplicateAsync( request.FirstName, request.LastName, request.Nickname, cancellationToken); } }
如果想进一步简化请求类,依然可以结合方案1的值对象,让请求传递PersonFullIdentity,领域服务接收值对象参数。
内容的提问来源于stack exchange,提问作者rafe
相关产品推荐
相关产品推荐

