在 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<IOrder>
基本上更改汇编扫描仪的代码
【参考方案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);
注意 IOrder
和 Command
在 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 注册为 IValidatorCommand
继承自 IOrder
您是说验证器列表未正确填充,因为 CommandValidator
是 IValidator<Command>
而不是 IValidator<IOrder>
跨度>
@johnny5,是的,MediatR 根据具体的命令类型而不是接口解析验证器。因此,在 .NET Core 容器中,两个验证器都必须注册为 IValidator<Command>
。对于必须由OrderValidator
验证的所有命令,也必须这样做。以上是关于在 MediatR 管道上使用多个 FluentValidators的主要内容,如果未能解决你的问题,请参考以下文章