Использование Fluent Results в конвейере валидации MediatR для возврата результата <TResponse> - PullRequest
0 голосов
/ 12 июля 2020

Во-первых, я использую Fluent Results в сочетании с Mediatr и Fluent Validation

Сначала я следил за this статья , но вместо того, чтобы изобретать колесо, я начал использовать FluentResults в своем конвейере Fluent Validation. В основном все ответы, поступающие от моих запросов CQRS, обертываются в объекте Result, это позволяет избежать работы с исключениями в качестве метода обработки ошибок.

Однако я не могу заставить свой конвейер работать хорошо:

public class ValidationPipeline<TRequest, TResponse>
    : IPipelineBehavior<TRequest, TResponse>
    where TResponse : class
    where TRequest : IRequest<TResponse>
{
    private readonly IValidator<TRequest> _compositeValidator;

    public ValidationPipeline(IValidator<TRequest> compositeValidator)
    {
        _compositeValidator = compositeValidator;
    }

    public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
    {
        var result = await _compositeValidator.ValidateAsync(request, cancellationToken);

        if (!result.IsValid)
        {
            Error error = new Error();
            var responseType = typeof(TResponse);

            foreach (var validationFailure in result.Errors)
            {
                Log.Warning($"{responseType} - {validationFailure.ErrorMessage}");
                error.Reasons.Add(new Error(validationFailure.ErrorMessage));
            }
            // This always returns null instead of a Result with errors in it. 
            var f = Result.Fail(error) as TResponse;
            return f;

        }

        return await next();
    }
}

Мне также нужно как-то преобразовать объект Result обратно в TResponse, где TResponse всегда является результатом

Любые предложения приветствуются!

Edit:

Автофайл c Интеграция

    protected override void Load(ContainerBuilder builder)
    {
        var assembly = Assembly.GetExecutingAssembly();

        // MediatR
        builder.AddMediatR(assembly);
        // Register the Command's Validators (Validators based on FluentValidation library)
        builder.RegisterAssemblyTypes(assembly)
            .Where(t => t.IsClosedTypeOf(typeof(IValidator<>)))
            .AsImplementedInterfaces();
        // Register all the Command classes (they implement IRequestHandler) in assembly holding the Commands
        builder.RegisterAssemblyTypes(assembly)
            .AsClosedTypesOf(typeof(IRequestHandler<,>));
        // Register Behavior Pipeline
        builder.RegisterGeneric(typeof(ValidationPipeline<,>)).As(typeof(IPipelineBehavior<,>));

    }

Ответы [ 3 ]

2 голосов
/ 12 июля 2020

Вы должны изменить

где TResponse: class

на

, где TResponse: Result

и убедиться, что все ваши запросы являются IRequest

, где T - фактический ответ, который вы хотите вернуть.

1 голос
/ 14 июля 2020

Я (сопровождающий FluentResults) и другой пользователь FluentResults / Mediatr добавили возможные решения в проблему с github. Вот ссылка: https://github.com/altmann/FluentResults/issues/54

ОБНОВЛЕНИЕ: Вот полный функциональный пример https://github.com/altmann/FluentResults#mediatr -request-handlers-return-result-objects

0 голосов
/ 12 июля 2020

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

Мне удалось выделить проблему в следующей строке:

 var f = Result.Fail(error).ToResult<CustomClass>() as TResponse;
 return f;

Если я жестко ссылаюсь на класс, который передаю в Result, тогда преобразование работает должным образом, и все работает так, как должно. Теперь возникает вопрос: как я могу получить ссылку на класс, которую я могу передать в .ToResult<T>() только из TResponse?

Я искал ответ, но, похоже, принципиально невозможно получить компиляцию ссылка на класс времени, во время выполнения, когда мне это понадобится во время компиляции.

Я также попытался создать копию объекта результата и вернуть его после добавления ошибок проверки. Примерно так:

 var resultType = typeof(TResponse).GetGenericArguments()[0];
 var invalidResponseType = typeof(ValidateableResponse<>).MakeGenericType(resultType);
 var f = Activator.CreateInstance(invalidResponseType, null) as TResponse;
 return f;

Это будет работать, но объект Result имеет закрытый конструктор, и поэтому я остался с исключением. Я оставил проблему на GitHub или FluentResult, и, возможно, ее можно изменить. *

Пример использования:

  // At the top of my handle function
  var result = await ValidateAsync<GetItemByIdQuery, GetItemByIdQueryValidator>(request);
  if (result.IsFailed) return result;

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

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

Спасибо всем за предложения!

...