.NET 6中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

