jbogard / MediatR.Extensions.Microsoft.DependencyInjection

MediatR extensions for Microsoft.Extensions.DependencyInjection
MIT License
327 stars 90 forks source link

Question: IPipelineBehavior execution sequence #105

Closed Naxaliav closed 2 years ago

Naxaliav commented 2 years ago

Hello 👋 I was looking into source code of MediatR and MediatR.Extensions.Microsoft.DependencyInjection and I could not find the place where you ensure that pipelineBehaviors are executed in the right order.

On command execution, all related IPipelineBehaviors are executed through pipeline. ref

 return serviceFactory
                .GetInstances<IPipelineBehavior<TRequest, TResponse>>()
                .Reverse()
                .Aggregate((RequestHandlerDelegate<TResponse>) Handler, (next, pipeline) => () => pipeline.Handle((TRequest)request, cancellationToken, next))();

Example code: (with 1x preprocessor, 2x postprocessor which just prints to console and are not visible in code snippet)

        static async Task Main()
        {
            var services = new ServiceCollection();

            services
                .AddMediatR(typeof(SimpleHandler))
                .AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingOne<,>))
                .AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingTwo<,>));

            var provider = services.BuildServiceProvider();

            var behaviors = provider.GetRequiredService<IEnumerable<IPipelineBehavior<CommandExample, Unit>>>();
            Console.WriteLine($"IPipelineBehaviors in container:\n\n{string.Join("\n", behaviors.Select(behavior => behavior.GetType().Name))}\n");

            Console.WriteLine("Sending command\n");
            var mediator = provider.GetRequiredService<IMediator>();
            await mediator.Send(new CommandExample());

            Console.ReadKey();
        }

Produces output: image

Looking into example above, the sequence how PipelineBehaviors are executed is somehow 0,4,5,1,2,3. I tried changing the order of registration, but it worked good at all times. I tried to look into source code, I could not find a place where you ensure that pre processor behavior is executed before any other additionally registered behavior. Same goes to post processor, it is always executed after all pipeline behaviors, it does not matter whether they are built in or custom.

Where is the magic? 😁

I have looked into test classes and found " / It appears Autofac returns the last registered types first", so do you somehow rely on how it is registered to DI? ref

jbogard commented 2 years ago

I don't ensure anything, this is a feature of the container you decide to use. In fact, all container registration/resolution functionality is 100% delegated to the container. This library doesn't rely on anything or make any assumptions.

Naxaliav commented 2 years ago

Thank you @jbogard :)