Mediatr handlers are they singletons?
Asked Answered
G

4

8

I am using the Mediatr in my .Net Core project and I was wondering if the handler's in the Mediatr are singleton's or are the new instances for every Send request; I know the Mediatr is a Singleton' but for the handlers it uses for a command or query, I am not very sure.

I tend to think they would also be singletons; but just wanted to double confirm.

Gosse answered 24/2, 2020 at 22:17 Comment(0)
M
6

In fact, lifetime of all those things are it's well documented https://github.com/jbogard/MediatR.Extensions.Microsoft.DependencyInjection/blob/master/README.md

Just for reference: IMediator is transient (not a singleton), IRequestHandler<> concrete implementations is transient and so on so actually it's transient everywhere.

But be aware of using Scoped services with Mediatr handlers, it works not as expected, more like singletons, unless you manually create a scope.

Monicamonie answered 15/7, 2020 at 22:32 Comment(0)
M
5

For the handlers, after following the source code, it looks like they are all added as Transient.

https://github.com/jbogard/MediatR.Extensions.Microsoft.DependencyInjection/blob/1519a1048afa585f5c6aef6dbdad7e9459d5a7aa/src/MediatR.Extensions.Microsoft.DependencyInjection/Registration/ServiceRegistrar.cs#L57

services.AddTransient(@interface, type);

For the IMediator itself, it looks like it is lifetime by default :

https://github.com/jbogard/MediatR.Extensions.Microsoft.DependencyInjection/blob/1519a1048afa585f5c6aef6dbdad7e9459d5a7aa/src/MediatR.Extensions.Microsoft.DependencyInjection/Registration/ServiceRegistrar.cs#L223

services.Add(new ServiceDescriptor(typeof(IMediator), serviceConfiguration.MediatorImplementationType, serviceConfiguration.Lifetime));

Note that the service configuration is a configuration object that unless somehow you change it along it's default path, will be set to transient too :

public MediatRServiceConfiguration()
{
    MediatorImplementationType = typeof(Mediator);
    Lifetime = ServiceLifetime.Transient;
}
Marcimarcia answered 25/2, 2020 at 0:41 Comment(0)
R
0

Using core you can manually register your handlers and use whatever scope you want. So for example:

services.AddScoped<IPipelineBehavior<MyCommand>, MyHandler>();

We actually wrap Mediatr so we can add various bits and bobs so it ends up being a registration extension like this (CommandContect/QueryContext holds various stuff we use all the time and ExecutionResponse is a standard response so we can have standard post handlers that know what they are getting):

public static IServiceCollection AddCommandHandler<THandler, TCommand>(this IServiceCollection services)
            where THandler : class, IPipelineBehavior<CommandContext<TCommand>, ExecutionResponse>
            where TCommand : ICommand
        {
            services.AddScoped<IPipelineBehavior<CommandContext<TCommand>, ExecutionResponse>, THandler>();
            return services;
        }

Which is used like this:

services.AddCommandHandler<MyHandler, MyCommand>();

We have similar for queries (AddQueryHandler<.....)

Hope that helps

Regorge answered 9/3, 2020 at 22:24 Comment(0)
S
0

Add the following code after you done with your container builder to see the truth:

foreach (var service in builder.Services)
{
   Console.WriteLine($"Service: {service.ServiceType.Name}, Lifetime: {service.Lifetime}");
}

Spoiler: as you will see, despite what Mediatr docs saying, everything is Singleton. And to my knowledge, this is not configurable, so you have to inject some kind of factories instead of scoped dependencies directly.
Here's my take on this:

private static IServiceCollection AddScopedFactory<T>(this IServiceCollection services) where T : class
{
    return services.AddSingleton<Func<T>>(s => () =>
       s.GetRequiredService<IHttpContextAccessor>()
        .HttpContext!
        .RequestServices
        .GetRequiredService<T>());
}

And then you can just register your precious EF context like this:

services.AddScopedFactory<MyCoolDbContext>();

Mediatr handler dependency will be Func<MyCoolDbContext> _getContext; and you just inject it and call it to obtain that context wherever needed inside handler.

Supination answered 20/11, 2023 at 15:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.