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

.NET 6中MediatR的GamerDtoValidator未注册的原因及修复方案

MediatR中GamerDtoValidator未生效的排查与修复方案

问题背景

我为MediatR定义了两个FluentValidation验证器:

  • GamerDtoValidator:继承AbstractValidator<GamerDto>,用于验证GamerDto对象
  • CreateGamerCommandValidator:继承AbstractValidator<CreateGamerCommand>,用于验证MediatR命令

已完成服务注册并配置了ValidatorBehavior管道中间件,但只有CreateGamerCommandValidator能被注册并生效,GamerDtoValidator始终被忽略。曾通过在CreateGamerCommandValidator中手动实例化GamerDtoValidator并嵌套调用临时解决,但该方案不合理,需寻求正确的排查与修复方法。

原因排查

1. ValidatorBehavior的作用范围限制

当前的ValidatorBehavior是针对IRequest<TResponse>类型设计的,它只会注入并执行与请求类型(如CreateGamerCommand)匹配的IValidator<TRequest>。而GamerDto并非MediatR请求类型,因此ValidatorBehavior不会主动触发GamerDtoValidator的验证逻辑。

2. 服务注册的潜在问题

扫描注册代码可能未正确识别GamerDtoValidator的接口实现:

  • 检查AssemblyMarkerTypes是否包含GamerDtoValidator所在程序集的标记类型
  • 扫描规则中的AddClasses(false)是否排除了GamerDtoValidator(false表示不包含抽象类,但GamerDtoValidator是具体类,理论上没问题,但需确认程序集扫描范围)

3. 验证触发逻辑缺失

即使GamerDtoValidator被注册到DI容器,若没有在命令处理流程中显式触发其验证,它也不会自动执行。

修复方案

方案1:正确关联命令验证器与Dto验证器(推荐)

无需手动映射Dto,直接在CreateGamerCommandValidator中通过依赖注入关联GamerDtoValidator,同时确保命令结构合理(若命令包含GamerDto属性):

调整后的CreateGamerCommandValidator

public class CreateGamerCommandValidator : AbstractValidator<CreateGamerCommand>
{
    private readonly IGamerRepository _gamerRepository;
    private readonly IValidator<GamerDto> _gamerDtoValidator;

    // 通过构造函数注入GamerDtoValidator
    public CreateGamerCommandValidator(IGamerRepository gamerRepository, IValidator<GamerDto> gamerDtoValidator)
    {
        _gamerRepository = gamerRepository;
        _gamerDtoValidator = gamerDtoValidator;

        // 若CreateGamerCommand包含GamerDto类型的属性,直接关联验证器
        RuleFor(x => x.GamerDto).SetValidator(_gamerDtoValidator);

        // 优化异步验证逻辑,避免同步阻塞
        RuleFor(v => v.Username).Must(async (gamer, cancellation) =>
        {
            var gamerExists = await _gamerRepository.AnyAsync(y => y.Username == gamer.Username).ConfigureAwait(false);
            return !gamerExists;
        }).WithMessage("User already exist - {PropertyValue}");
    }
}

方案2:确保GamerDtoValidator被正确注册

如果扫描注册未生效,可显式注册验证器到DI容器:

// 在服务注册代码中添加
services.AddTransient<IValidator<GamerDto>, GamerDtoValidator>();

方案3:优化ValidatorBehavior支持嵌套验证(可选)

若需要自动触发嵌套对象的验证,可修改ValidatorBehavior,使用FluentValidation的功能递归验证对象(仅在有大量嵌套验证需求时考虑):

public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
{
    var context = new ValidationContext<TRequest>(request);
    var validationResults = await Task.WhenAll(_validators.Select(v => v.ValidateAsync(context, cancellationToken)));
    var failures = validationResults.SelectMany(r => r.Errors).Where(f => f != null).ToList();

    if (failures.Any())
    {
        _logger.LogWarning("Validation failed for request {RequestType}", typeof(TRequest).Name);
        throw new ValidationException(failures);
    }

    return await next();
}

相关代码参考

初始GamerDtoValidator

public class GamerDtoValidator : AbstractValidator<GamerDto>
{
    public GamerDtoValidator()
    {
        RuleFor(v => v.Username)
            .NotEmpty().WithMessage("Username is required")
            .MaximumLength(50).WithMessage("Username exceeds the permitted limit");
    }
}

初始服务注册代码

services.AddMediatR(typeof(Program));

services.Scan(s => s
                .FromAssembliesOf(this.AssemblyMarkerTypes)
                .AddClasses(false)
                .UsingRegistrationStrategy(RegistrationStrategy.Append)
                .AsImplementedInterfaces()
                .WithTransientLifetime());

完整ValidatorBehavior代码

public class ValidatorBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    private readonly ILogger<ValidatorBehavior<TRequest, TResponse>> _logger;
    private readonly IEnumerable<IValidator<TRequest>> _validators;

    public ValidatorBehavior(IEnumerable<IValidator<TRequest>> validators, ILogger<ValidatorBehavior<TRequest, TResponse>> logger)
    {
        _validators = validators;
        _logger = logger;
    }

    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
    {
        var context = new ValidationContext<TRequest>(request);
        var validationResults = await Task.WhenAll(_validators.Select(v => v.ValidateAsync(context, cancellationToken)));
        var failures = validationResults.SelectMany(r => r.Errors).Where(f => f != null).ToList();

        if (failures.Any())
        {
            _logger.LogWarning("Validation failed for request {RequestType}", typeof(TRequest).Name);
            throw new ValidationException(failures);
        }

        return await next();
    }
}

内容的提问来源于stack exchange,提问作者One Developer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 07:48:16