jbogard / MediatR

Simple, unambitious mediator implementation in .NET
Apache License 2.0
11k stars 1.16k forks source link

IPipelineBehavior not being executed #879

Closed kirnosenko closed 1 year ago

kirnosenko commented 1 year ago

Prior to 12 everything worked fine but now I can't make pipeline behaviors work. I register them like this: services.AddMediatR(c => { c.RegisterServicesFromAssembly(typeof(Startup).Assembly); c.AddOpenBehavior(typeof(LoggingBehaviour<,>)); c.AddOpenBehavior(typeof(ExceptionBehaviour<,>)); });

But no calls for them. The handler itself still works. What am I missing?

5wag commented 1 year ago

I did some investigation on this
It doesn't work for me with the setup as following

public class Command : IRequest {}

public class CommandHandler : IRequestHandler<Command> {/*...*/}

public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
        where TRequest : IRequest<TResponse>
    {
        private readonly ILogger<LoggingBehavior<TRequest, TResponse>> _logger;

        public LoggingBehavior(ILogger<LoggingBehavior<TRequest, TResponse>> logger)
        {
            _logger = logger;
        }

        public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
        {
            _logger.LogInformation($"Handling {typeof(TRequest).Name}");
            var response = await next();
            _logger.LogInformation($"Handled {typeof(TResponse).Name}");

            return response;
        }
    }

services.AddMediatR(cfg =>
        {
            cfg.AddOpenBehavior(typeof(LoggingBehavior<,>));
            cfg.RegisterServicesFromAssembly(typeof(LoggingBehavior).Assembly);
        });

Once I change my command and command handler to be as below it is working as expected

public class Command : IRequest<Unit> {}

public class CommandHandler : IRequestHandler<Command, Unit> {/*...*/}

MediatR version is 12.0.1 .NET 7.0.200 (ASPNET SDK)

jbogard commented 1 year ago

Check the upgrade guide, your generic constraints are off.

5wag commented 1 year ago

Indeed, thanks and sorry for bothering

jbogard commented 1 year ago

No worries, that was a huge but silent breaking change. Maybe I need an even bigger warning!

5wag commented 1 year ago

I think this page needs update https://github.com/jbogard/MediatR/wiki/Behaviors

If I understand the upgrade doc correctly behaviors should now have constraint where TRequest : notnull. The example on the page still does where TRequest : IRequest<TResponse> which was misleading to me

isnakolah commented 1 year ago

Check the upgrade guide, your generic constraints are off.

To save anyone the trip, here's the migration docs

sudheshg commented 1 year ago

Not sure what I am missing; I followed the migration guide, and still, pipeline behavior is not invoked, but handler works.

public class ValidationPipelineBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
        where TRequest : notnull

builder.Services.AddMediatR(cfg => {
                cfg.RegisterServicesFromAssembly(typeof(Program).Assembly);
                cfg.AddBehavior(typeof(IPipelineBehavior<,>),typeof(ValidationPipelineBehavior<,>));
            });

MediatR version is 12.0.1 .NET 6.0 MS DI

Please help me find what I'm missing and confirm that it was working prior to version 12.

isnakolah commented 1 year ago

Not sure what I am missing; I followed the migration guide, and still, pipeline behavior is not invoked, but handler works.

public class ValidationPipelineBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
        where TRequest : notnull

builder.Services.AddMediatR(cfg => {
                cfg.RegisterServicesFromAssembly(typeof(Program).Assembly);
                cfg.AddBehavior(typeof(IPipelineBehavior<,>),typeof(ValidationPipelineBehavior<,>));
            });

MediatR version is 12.0.1 .NET 6.0 MS DI

Please help me find what I'm missing and confirm that it was working prior to version 12.

This code is valid, the bug, as far as I can tell, has nothing to do with its registration. Check your pipeline behavior for any early exists. I have recreated that in this gist. Have a look to see if there's any differences.

sudheshg commented 1 year ago

@isnakolah Thank you and it is working . The external interface which i created the IPipelineBehavior is caused the issue and it is not required.

Anakael commented 1 year ago

If change:

public class GenericPipelineBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
-    where TRequest : IRequest<TResponse>
+    where TRequest : notnull
{

then in Handle method such code won't work.

IRequestHandler<TRequest, TResponse> handler = scope.ServiceProvider.GetRequiredService<IRequestHandler<TRequest, TResponse>>();

To fix this you can use:

Type handlerType = typeof(IRequest).IsAssignableFrom(typeof(TRequest))
            ? typeof(IRequestHandler<>).MakeGenericType(typeof(TRequest))
            : typeof(IRequestHandler<,>).MakeGenericType(typeof(TRequest), typeof(TResponse));
        object handler = scope.ServiceProvider.GetRequiredService(handlerType);
blackhawk64 commented 1 year ago

I just had a problem in the past days. I was implementing validations with Fluent Validations but the PipelineBehavior seemed not to work. So after a very very long journey, I made this change to my code:

From this: where TRequest : IRequest<TResponse>

To this: where TRequest : notnull

Also, need to say that I was implementing 2 types of commands: IRequest<TResponse> and IRequest. I've tried to use something like: where TRequest : IRequest<TResponse>, IRequest

but to save you guys the work, if you've been trying that (just like me hehe), it will not work Hope this could help someone :)

jacobjmarks commented 1 year ago

@blackhawk64 with respect to supporting both IRequest<TResponse> and IRequest, you can constrain against IBaseRequest which both of these types inherit.

i.e. where TRequest : IBaseRequest

jonaskowalczyk commented 1 year ago

@jacobjmarks Thank you very much, you saved my day being depressed.