Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 917
rideintothesun Avatar asked Jul 12 '19 14:07

rideintothesun


People also ask

Is MediatR necessary?

MediatR provides you with other functionality as well. It supports notifications mechanism. It may be very useful if you use domain events in your architecture. All classes of your events must implement INotification marker interface.

Is MediatR an anti pattern?

Most of the time it's used like a glorified Service Locator, which is notoriously an anti-pattern.

Is MediatR a CQRS?

The MediatR library was built to facilitate two primary software architecture patterns: CQRS and the Mediator pattern. Whilst similar, let's spend a moment understanding the principles behind each pattern.

Is MediatR a singleton?

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.


1 Answers

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());
}
like image 145
djjlewis Avatar answered Sep 24 '22 09:09

djjlewis