You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 06:22:50