Добавить проверку к конвейеру поведения MediatR?


Я использую ASP.NET ядро, встроенный контейнер и MediatR 3, который поддерживает "поведенческие" конвейеры :

public class MyRequest : IRequest<string>
{
    // ...
}

public class MyRequestHandler : IRequestHandler<MyRequest, string>
{
    public string Handle(MyRequest message)
    {
        return "Hello!";
    }
}

public class MyPipeline<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next)
    {
        var response = await next();
        return response;
    }
}

// in `Startup.ConfigureServices()`:
services.AddTransient(typeof(IPipelineBehavior<MyRequest,str‌​ing>), typeof(MyPipeline<MyRequest,string>))

Мне нужен валидатор FluentValidation в конвейере. В MediatR 2 конвейер проверки был создан таким образом:

public class ValidationPipeline<TRequest, TResponse>
    : IRequestHandler<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{

    public ValidationPipeline(IRequestHandler<TRequest, TResponse> inner, IEnumerable<IValidator<TRequest>> validators)
    {
        _inner = inner;
        _validators = validators;
    }

    public TResponse Handle(TRequest message)
    {
        var failures = _validators
            .Select(v => v.Validate(message))
            .SelectMany(result => result.Errors)
            .Where(f => f != null)
            .ToList();
        if (failures.Any())
            throw new ValidationException(failures);
        return _inner.Handle(request);
    }

}

Как это сделать теперь для новой версии? Как установить, какой валидатор использовать?

1 12

1 ответ:

Процесс точно такой же, вам просто нужно изменить интерфейс, чтобы использовать новый интерфейс IPipelineBehavior<TRequest, TResponse>.

public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    private readonly IEnumerable<IValidator<TRequest>> _validators;

    public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
    {
        _validators = validators;
    }

    public Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next)
    {
        var context = new ValidationContext(request);
        var failures = _validators
            .Select(v => v.Validate(context))
            .SelectMany(result => result.Errors)
            .Where(f => f != null)
            .ToList();

        if (failures.Count != 0)
        {
            throw new ValidationException(failures);
        }

        return next();
    }
}

Для валидаторов вы должны зарегистрировать все валидаторы как IValidator<TRequest> во встроенном контейнере, чтобы они были введены в поведение. Если вы не хотите регистрировать их по одному, я предлагаю вам взглянуть на большую библиотекуScrutor , которая предоставляет возможности сканирования сборок. Таким образом, он сам найдет ваши валидаторы.

Также, с новым система, вы больше не используете шаблон декоратора, вы просто регистрируете свое общее поведение в контейнере, и MediatR автоматически его подберет. Это может выглядеть примерно так:

var services = new ServiceCollection();
services.AddMediatR(typeof(Program));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
var provider = services.BuildServiceProvider();