asp.net-coredependency-injectionmediatrlifetime-scoping

Mediatr Scope problems


I am using Mediatr to handle messages from a queue. I can get a simple example to work. However I have run into problems when I try to inject an object into my handler

public class MessageCommandHandler : IRequestHandler<MessageCommand, bool>
{
    private IMyDependency myDependency;
    public MessageCommandHandler(IMyDependency myDependency)
    {
        this.myDependency = myDependency;
    }
    
    public Task<bool> Handle(MessageCommand request, CancellationToken cancellationToken)
    {
        return Task.FromResult(true);
    }
}

This only works when I register IMyDependency as a transient scope, however when I register it as scoped lifetime it fails with the error

Cannot resolve 'MediatR.IRequestHandler`2[MyNamespace.MessageCommand,System.Boolean]' from root provider because it requires scoped service 'MyNamespace.IMyDependency'

I need to be able to inject dependencies with scoped lifetime. Has anyone got a solution for this.

I am using the .NET Core dependency injection framework. It is setup as follows

services.AddHostedService<QueueConsumer>();
services.AddScoped<IMyDependency, MyDependency>();
services.AddMediatR(new Assembly[] { Assembly.GetExecutingAssembly() });

Any ideas?


Solution

  • Any time you use a dependency with a Scoped lifetime, you will need to use it inside a pre-created scope. In the case of MVC this would happen automatically behind the scenes but if you're using direct from your own code, say via a console application or something, you will need to create the scope yourself.

    This can be done by injecting an instance of IServiceScopeFactory and then using this factory to create a scope and then retrieve the dependency from that scope e.g.

    public class MessageCommandHandler : IRequestHandler<MessageCommand, bool>
        {
            private IServiceScopeFactory _serviceScopeFactory;
    
            public MessageCommandHandler(IServiceScopeFactory serviceScopeFactory) 
    
            {
                _serviceScopeFactory = serviceScopeFactory;
            }
    
            public Task<bool> Handle(MessageCommand request, CancellationToken cancellationToken)
            {
                using (var scope = _serviceScopeFactory.CreateScope())
                {
                    var scopedServices = scope.ServiceProvider;
                    var myDependency = scopedServices.GetRequiredService<IMyDependency>();
                    return Task.FromResult(true);
                }
            }
        }
    

    However (and note that the code above is untested), in my own systems I would almost always create the scope around whatever is sending the mediator request in which case any Scoped dependencies will still be injected automatically at this scope e.g.

    ... // some other calling class / Main method etc..
    
    using (var scope = _serviceScopeFactory.CreateScope())
        var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
        mediator.Send(new MessageCommand());
    }