Search code examples
dependency-injectionasp.net-coremediatr

Unable to register IRequestPreProcessors with Mediatr


I want to register the following dummy 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);
    }
}

To do so, I have my container setup to map IRequestPreProcessor to my concrete implementation 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<,>));

As soon as I run the code, I get the following exception

System.ArgumentException: Open generic service type 'MediatR.Pipeline.IRequestPreProcessor`1[TRequest]' requires registering an open generic implementation type.

I want to run this pre-processor only for commands of type RegisterUserCommand. Any idea on how I can solve this issue?

FYI for the records,

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;
        }
    }
}

Thank you, Regards, Seb


Solution

  • So after talking to the lib's creator, it seems that Pre & Post processors must have a generic definition.

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

    OR

    public class GenericRequestPreProcessor<TRequest> : IRequestPreProcessor<TRequest>
    

    src: Examples

    The <TRequest> and <TRequest, TResponse> are required when creating a new Pre/Post processors. Now the 1 000 000$ dollars question I will try to answer later:

    How do we specialize processors so they deal with a specific set of requests/commands (without having to check request type)...