我在ASP.NET Core和新的MediatR which supports pipelines上。我的管道includes validation。
考虑这个动作:
[HttpPost]
[HandleInvalidCommand]
public IActionResult Foo(Command command)
{
await _mediator.Send(command);
return View();
}
HandleInvalidCommand
检查ModelState.IsValid
,如果无效,则重定向到视图以供用户更正数据因此,如果命令有效,则验证会发生两次(验证器的运行成本很高)。
我如何才能最好地解决这个问题?
编辑:显而易见的方法是从管道中删除验证,但这并不好,因为该命令可能来自UI,但也来自应用程序本身。并且您希望在两种情况下进行验证。
答案 0 :(得分:1)
FluentValidation
即使验证失败也不会停止处理您的命令 - 它只是注册规则。
Mediatr Validation Pipeline
检查现有验证错误并停止发送命令 - 如果存在错误,Handler将不会触发。
但是你实现了自己的逻辑 - HandleInvalidCommand
。您应该选择一个选项 - mediatr pipiline或使用ModelState.IsValid
实现自己的逻辑
答案 1 :(得分:1)
我发现了另一种方式。也许不是最好的,但它确实有效。
定义此界面
public interface IValidated
{
bool AlreadyValidated { get; }
}
装饰请求
public class Command : IRequest, IValidated
{
public bool AlreadyValidated { get; set; }
// etc...
}
将请求的验证程序更新为use an interceptor:
public class CommandValidator : AbstractValidator<Command>, IValidatorInterceptor
{
public CommandValidator() {
// validation rules etc.
}
public ValidationContext BeforeMvcValidation(ControllerContext controllerContext, ValidationContext validationContext)
{
return validationContext;
}
public ValidationResult AfterMvcValidation(ControllerContext controllerContext, ValidationContext validationContext, ValidationResult result)
{
var command = validationContext.InstanceToValidate as Command;
if (command != null) command.AlreadyValidated = true;
return result;
}
}
更新管道:
public class MyPipeline<TRequest, TResponse>
: IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>, IValidated // update here
{
public async Task<TResponse> Handle(
TRequest message,
RequestHandlerDelegate<TResponse> next)
{
if (!message.AlreadyValidated) // update here
{
var context = new ValidationContext(message);
var failures = _validators
.Select(v => v.Validate(context))
.SelectMany(e => e.Errors)
.Where(e => e != null)
.ToList();
if (failures.Count != 0)
throw new ValidationException(failures);
}
return await next();
}
}
因此,在通过MVC / FluentValidation进行验证后,它会设置标志。然后在CQRS管道中,如果设置了该标志,则不再执行验证。
但是我不确定我喜欢这个,因为我正在泄漏那些不应该存在的命令。
答案 2 :(得分:0)
我认为理想的解决方案是将命令/查询与来自客户端的模型之间使用的类分开。这可能是最正确的设计,因为它使命令/查询类专用于应用程序核心输入,因此不会进行修改以适合客户端并随时间变化。当涉及到应用程序核心时,这将使您的CQRS类更加纯净。
但这确实意味着要重复更多的类,以便为客户的输入提供更多的类。