Search code examples
genericsdependency-injectionasp.net-coremediatr

Mediatr - Pre/post-processor specialization


I'm currently revamping some large controllers in our asp.net core app. To do so we chose Mediatr and we are currently splitting those big actions into handlers & pre/post processors.

Some of our commands need to trigger an internal notification system (node.js service). To do so, I have developed a post processor in charge of notifying the event service. However, I want to "trigger" it only for commands inheriting from the INotify interface. In other words, Mediatr loads all pre/post processors but it triggers only those whose command type is matching the generic constraint. In the end it would look like this:

 public class NotificationPostProcessor<TCommand, TResponse> : IRequestPostProcessor<TCommand, TResponse>
        where TCommand : >>INotifyCommand<<
        where TResponse : CommandResult
{
     (...)
}

If the command does not inherit from INotifyCommand then this post-processor is not triggered.

Same thing goes for pre-processors. For example, I would need my pre-processor to add some extra data to some specific command.

Currently what I do is horrible and I'm sure there is a better way.

public class NotificationPostProcessor<TCommand, TResponse> : IRequestPostProcessor<TCommand, TResponse>
    where TCommand : IRequest<TResponse>
    where TResponse : CommandResult
{
    private readonly INotificationService _service;

    public NotificationPostProcessor(INotificationService service)
    {
        _service = service;
    }

    public async Task Process(TCommand command, TResponse response)
    {
        var cmd = command as NotifyBaseCommand;
        if (cmd != null && response.IsSuccess)
            await _service.Notify(cmd.Event, command, response);
    }
}

Since I'm using the default asp.net core dependency injection engine + MediatR.Extensions.Microsoft.DependencyInjection package, I'm not directly registering Post & Pre processors.

   // Pipeline engine used internally to simplify controllers
    services.AddMediatR();
    // Registers behaviors
    services.AddTransient(typeof(IPipelineBehavior<,>), typeof(Pipeline<,>));
    services.AddTransient(typeof(IPipelineBehavior<,>), typeof(AuditBehavior<,>));
    services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestPreProcessorBehavior<,>));
    services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
    services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestPostProcessorBehavior<,>));
    // Registers command validator
    services.AddTransient(typeof(IValidator<RegisterUserCommand>), typeof(RegisterUserCommandValidator));

I'm must admit I'm a little bit lost here. Any idea on how i can improve this system?

Thank you, Sebastien


Solution

  • Apparently ASP.net core DI does not support this feature.

    SRC: Support constrained open generic types

    It worked with Autofac. I just had to add one single line of code :)

        var host = new WebHostBuilder()
            .UseKestrel()
            .ConfigureServices(services => services.AddAutofac())
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>()
            .UseApplicationInsights()
            .Build();