在 MediatR 管道上使用多个 FluentValidators

Posted

技术标签:

【中文标题】在 MediatR 管道上使用多个 FluentValidators【英文标题】:Using multiple FluentValidators on MediatR pipeline 【发布时间】:2019-10-21 11:47:04 【问题描述】:

我有这样的 MediatR 管道行为:

public class FailFastRequestBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>

    private readonly IEnumerable<IValidator> _validators;

    public FailFastRequestBehavior(IEnumerable<IValidator<TRequest>> validators)
    
        _validators = validators;
    

    public Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
    
        var failures = _validators
            .Select(async v => await v.ValidateAsync(request))
            .SelectMany(result => result.Result.Errors)
            .Where(f => f != null);


        return failures.Any()
            ? Errors(failures)
            : next();
    

    ...

MediatR 命令如下:

public class MyUseCase

    public class Command : IRequest<CommandResponse>
    
        ...
    

    public class Validator : AbstractValidator<Command>
    
        ...
    

    public class Handler<T>: IRequestHandler<T, CommandResponse>
    
        ...
    

验证器在Startup.cs 上注册,如下所示:

        AssemblyScanner
          .FindValidatorsInAssembly(Assembly.GetAssembly(typeof(MyUseCase)))
            .ForEach(result => 
                services.AddScoped(result.InterfaceType, result.ValidatorType));

这适用于MyUseCase.Validator,它被注入管道并被执行,验证MyUseCase.Command

但它是一个大型应用程序,许多命令都有共同的属性,即每个订单操作都会收到一个OrderId,我必须检查 Id 是否有效,实体是否存在于数据库中,经过身份验证的用户是否是所有者正在修改的订单等。

所以我尝试创建以下接口和验证器:

public interface IOrder

    string OrderId  get; set; 


public class IOrderValidator : AbstractValidator<IOrder>

    public IOrderValidator()
    
        CascadeMode = CascadeMode.StopOnFirstFailure;

        RuleFor(x => x.OrderId)
            .Rule1()
            .Rule2()
            .Rule3()
            .RuleN()
     

最后我把命令改成了这样:

public class MyUseCase

    public class Command : IRequest<CommandResponse>: IOrder
    
        ...
    

    public class Validator : AbstractValidator<Command>
    
        ...
    

    public class Handler<T>: IRequestHandler<T, CommandResponse>
    
        ...
    

问题是IOrderValidator没有注入管道,只有MyUseCase.Validator是。

我在这里遗漏了什么,或者甚至可以在管道中注入多个验证器吗?

【问题讨论】:

您只需将IOrderValidator注册为IValidator&lt;IOrder&gt; 基本上更改汇编扫描仪的代码 【参考方案1】:

服务解析取决于您使用的 DI 容器。您似乎使用了内置的 .NET Core 容器,它无法解析逆变接口。

请考虑 Simple Injector,因为它知道如何处理逆变换。此示例代码将解析您需要的所有验证器:

[Fact]
public void Test()

    var container = new SimpleInjector.Container();

    container.Collection.Append<IValidator<IOrder>, OrderValidator>();
    container.Collection.Append<IValidator<Command>, CommandValidator>();

    var validators = container.GetAllInstances<IValidator<Command>>();

    validators.Should().HaveCount(2);

或者,您必须显式注册您的验证器,并使用它们必须适用的所有命令参数化:

[Fact]
public void Test()

    var provider = new ServiceCollection()
        .AddTransient(typeof(IValidator<Command>), typeof(OrderValidator))
        .AddTransient(typeof(IValidator<Command>), typeof(CommandValidator))
        .BuildServiceProvider();

    var validators = provider.GetServices<IValidator<Command>>();

    validators.Should().HaveCount(2);

注意 IOrderCommand 在 Simple Injector 和 .NET Core DI 容器中注册 OrderValidator 的区别:

container.Collection.Append<IValidator<IOrder>, OrderValidator>();
servcies.AddTransient(typeof(IValidator<Command>), typeof(OrderValidator))

假设定义了以下类和接口:

interface IOrder



class Command : IRequest<CommandResponse>, IOrder



class CommandResponse



class OrderValidator : AbstractValidator<IOrder>



class CommandValidator : AbstractValidator<Command>


【讨论】:

.Net-Core DI 有同样的支持 您只需将 IOrderValidator 注册为 IValidator @johnny5,不,你可以自己试试。安装所需的 MediatR、FluentValidation 和 FluentAssertion 包并运行测试。 哦,我现在明白了,Command 继承自 IOrder 您是说验证器列表未正确填充,因为 CommandValidatorIValidator&lt;Command&gt; 而不是 IValidator&lt;IOrder&gt;跨度> @johnny5,是的,MediatR 根据具体的命令类型而不是接口解析验证器。因此,在 .NET Core 容器中,两个验证器都必须注册为 IValidator&lt;Command&gt;。对于必须由OrderValidator 验证的所有命令,也必须这样做。

以上是关于在 MediatR 管道上使用多个 FluentValidators的主要内容,如果未能解决你的问题,请参考以下文章

Mediatr 行为管道中的验证

使用mediatR管道行为添加验证。

使用 void/Task 响应注册 MediatR 管道

向 MediatR 行为管道添加验证?

如何为 ASP.NET Core 注册和使用 MediatR 管道处理程序?

来自管道行为的 MediatR 流畅的验证响应