Невозможно зарегистрировать IRequestPreProcessors в Mediatr

Я хочу зарегистрировать следующий фиктивный IRequestPreProcessor (Mediator 3)

public class IdentifyUserTypeCommandHandler : IRequestPreProcessor<RegisterUserCommand>
{
    private readonly IOptions<TecApiOptions> _options;

    public IdentifyUserTypeCommandHandler(IOptions<TecApiOptions> options)
    {
        _options = options;
    }

    public async Task Process(RegisterUserCommand request)
    {
        request.Type = "internal";
        await Task.FromResult(true);
    }
}

Для этого у меня есть настройки контейнера для сопоставления IRequestPreProcessor с моей конкретной реализацией IdentifyUserTypeCommandHandler

        // Pipeline engine used internally to simplify controllers
        services.AddMediatR();
        // Pre-processors
        services.AddTransient(typeof(IRequestPreProcessor<RegisterUserCommand>), typeof(IdentifyUserTypeCommandHandler));

        // Registers command validator
        services.AddTransient(typeof(IValidator<RegisterUserCommand>), typeof(RegisterUserCommandValidator));

        // Registers generic behaviors
        services.AddTransient(typeof(IPipelineBehavior<,>), typeof(Pipeline<,>));
        services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
        services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
        services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestPreProcessorBehavior<,>));

Как только я запускаю код, я получаю следующее исключение

System.ArgumentException: для открытия универсального типа службы "MediatR.Pipeline.IRequestPreProcessor`1[TRequest]" требуется зарегистрировать открытый универсальный тип реализации.

Я хочу запустить этот препроцессор только для команд типа RegisterUserCommand. Любая идея о том, как я могу решить эту проблему?

К вашему сведению,

public class LoggingBehavior<TCommand, TResponse> : IPipelineBehavior<TCommand, TResponse>
{
    private readonly ILogger _logger;

    public LoggingBehavior(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory?.CreateLogger(typeof(TCommand).Name) ?? throw new ArgumentNullException(nameof(loggerFactory));
    }

    public async Task<TResponse> Handle(TCommand request, RequestHandlerDelegate<TResponse> next)
    {
        try
        {
            _logger.LogInformation(LoggingEvents.RUN_HANDLER, $"Handling '{typeof(TCommand).Name}'");
            var response = await next();
            _logger.LogInformation(LoggingEvents.RUN_HANDLER, $"Handled '{typeof(TResponse).Name}'");
            return response;
        }
        catch (Exception e)
        {
            _logger.LogError(
                LoggingEvents.RUN_HANDLER_EXCEPTION, e,
                $"An error occured while processing pipeline '{GetType().Name}' [{typeof(TCommand).Name} >> {typeof(TResponse).Name}]");
            throw;
        }
    }
}

Спасибо, С уважением, Себ

3 ответа

Решение

Итак, после разговора с создателем библиотеки, похоже, что процессоры Pre и Post должны иметь общее определение.

public class GenericRequestPostProcessor<TRequest, TResponse> : IRequestPostProcessor<TRequest, TResponse>

ИЛИ ЖЕ

public class GenericRequestPreProcessor<TRequest> : IRequestPreProcessor<TRequest>

src: примеры

<TRequest> а также <TRequest, TResponse> необходимы при создании новых Pre/Post процессоров. Теперь на вопрос в 1 000 000 долларов я постараюсь ответить позже:

Как мы специализируем процессоры, чтобы они обрабатывали определенный набор запросов / команд (без необходимости проверять тип запроса)...

Если вы используете services.AddMediatR()

Убедитесь, что вы зарегистрировали встроенное поведение: RequestPreProcessorBehavior

services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestPreProcessorBehavior<,>));
services.AddMediatR();

Тогда ваш препроцессор должен иметь общее определение в соответствии с ответом @Sebastien:

public class PingDecorator<TRequest> : IRequestPreProcessor<TRequest>
{
    public Task Process(TRequest request)
    {
        return Unit.Task;
    }
}

Если вы хотите, чтобы ваш препроцессор использовался только с конкретным запросом:

public class PingDecorator<TRequest> : IRequestPreProcessor<TRequest> where TRequest:Ping
{
    public Task Process(TRequest request)
    {
        request.Message = request.Message + " Decorated";
        return Unit.Task;
    }
}

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

Другие вопросы по тегам