RBa*_*iak 7 c# fluentvalidation mediatr asp.net-core
我有一个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();
}
...
}
Run Code Online (Sandbox Code Playgroud)
像这样的MediatR命令:
public class MyUseCase
{
public class Command : IRequest<CommandResponse>
{
...
}
public class Validator : AbstractValidator<Command>
{
...
}
public class Handler<T>: IRequestHandler<T, CommandResponse>
{
...
}
}
Run Code Online (Sandbox Code Playgroud)
验证器的注册方式Startup.cs如下:
AssemblyScanner
.FindValidatorsInAssembly(Assembly.GetAssembly(typeof(MyUseCase)))
.ForEach(result =>
services.AddScoped(result.InterfaceType, result.ValidatorType));
Run Code Online (Sandbox Code Playgroud)
这对的效果很好,将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()
}
}
Run Code Online (Sandbox Code Playgroud)
最后,我将命令更改为:
public class MyUseCase
{
public class Command : IRequest<CommandResponse>: IOrder
{
...
}
public class Validator : AbstractValidator<Command>
{
...
}
public class Handler<T>: IRequestHandler<T, CommandResponse>
{
...
}
}
Run Code Online (Sandbox Code Playgroud)
问题在于,IOrderValidator不是仅将MyUseCase.Validatoris 注入管道中。
我是否在这里遗漏了一些东西,甚至有可能在管道中注入多个验证器?
服务分辨率取决于您使用的DI容器。似乎您使用了内置的.NET Core容器,并且它无法解析相反的接口。
请考虑使用简单喷射器,因为它知道如何使用协变。此示例代码将解析您需要的所有验证器:
[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);
}
Run Code Online (Sandbox Code Playgroud)
另外,您必须显式注册使用所有必须应用于以下命令的参数化验证器:
[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);
}
Run Code Online (Sandbox Code Playgroud)
请注意IOrder,Command对于OrderValidatorSimple Injector和.NET Core DI容器,它们之间的区别在于:
container.Collection.Append<IValidator<IOrder>, OrderValidator>();
servcies.AddTransient(typeof(IValidator<Command>), typeof(OrderValidator))
Run Code Online (Sandbox Code Playgroud)
假设定义了以下类和接口:
interface IOrder
{
}
class Command : IRequest<CommandResponse>, IOrder
{
}
class CommandResponse
{
}
class OrderValidator : AbstractValidator<IOrder>
{
}
class CommandValidator : AbstractValidator<Command>
{
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
164 次 |
| 最近记录: |