2017-02-21 18 views
1

Я нахожусь на ядре ASP.NET и в новом MediatR which supports pipelines. Мой трубопровод includes validation.Двойная проверка в конвейере MediatR

Рассмотрим это действие:

[HttpPost] 
[HandleInvalidCommand] 
public IActionResult Foo(Command command) 
{ 
    await _mediator.Send(command); 
    return View(); 
} 
  • Команда проверяется (я использую FluentValidation)
  • HandleInvalidCommand проверки ModelState.IsValid и если недействительна затем перенаправляет к просмотру для пользователей, чтобы исправить данные
  • Искомый пробег
  • Команда отправлена ​​в трубопровод
  • Трубопровод проверяет команду, AGAIN

Итак, если команда действительна, проверка выполняется дважды (и валидаторы дороги для запуска).

Как лучше всего с этим справиться?

EDIT: Очевидным способом является удаление валидации из конвейера, но это не хорошо, потому что команда может поступать из пользовательского интерфейса, а также из самого приложения. И вы хотите получить подтверждение в обоих случаях.

+0

Значит, атрибут 'HandleInvalidCommand' запускает проверку? –

+0

@ MickaëlDerriey Nope FluentValidation автоматически проверяет аргументы при привязке модели. Этот атрибут просто проверяет «ModelState.IsValid» и перенаправляет представление. Я отредактировал, чтобы сделать его более ясным. – grokky

+1

ОК. так что да, я думаю, вам нужно выбирать между запуском проверки как частью привязки модели или как часть конвейера MediatR. Это действительно * это зависит от ситуации. С одной стороны, наличие во время привязки модели облегчает работу с уровнем MVC с ошибками. С другой стороны, использование его как части конвейера MediatR делает его сквозной задачей, поэтому применимо, если вы решите подключить трубопровод в другом приложении/каркасе. Тем не менее, обработка ошибок может быть сложнее. Поведение проверки может вызвать исключение, которое мог бы обработать фильтр действий. –

ответ

0

Я нашел другой способ. Может быть, не самый лучший, но он работает.

Определить этот интерфейс

public interface IValidated 
{ 
    bool AlreadyValidated { get; } 
} 

Украсьте запрос

public class Command : IRequest, IValidated 
{ 
    public bool AlreadyValidated { get; set; } 
    // etc... 
} 

Update валидатор просьбе к 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, если этот флаг установлен, он не выполняет проверку снова.

Однако я не уверен, что мне это нравится, так как я просачиваю материал в команду, которой не должно быть.

+0

Почему вы просто не удаляете конвейер проверки Mediatr из своего приложения? Если вы обрабатываете каждую команду с помощью 'Interceptor', конвейер проверки бесполезен. – Pavel

+0

@Pavel См. Мои объяснения в приведенном выше правиле. Очевидным способом является удаление проверки из конвейера, но это не хорошо, потому что команда может поступать из пользовательского интерфейса, а также из самого приложения. И вы хотите получить подтверждение в обоих случаях. – grokky

+0

, когда вы говорите, что команда может поступать из самого приложения, вы хотите сказать, что вы отправите вторую команду из обработчика запроса/команды? –

1

FluentValidation не прекращает обработку вашей команды, даже если проверка не удалась - она ​​просто регистрирует правила.

Mediatr Validation Pipeline проверяет наличие существующих ошибок проверки и прекращает отправку команды. Обработчик не загорается, если существуют ошибки.

Но вы внедрили свою собственную логику - HandleInvalidCommand. Вы должны выбрать один вариант - mediatr pipiline или реализовать собственную логику с ModelState.IsValid