The Solution. To be able to use scoped services within a singleton, you must create a scope manually. A new scope can be created by injecting an IServiceScopeFactory into your singleton service (the IServiceScopeFactory is itself a singleton, which is why this works).
Essentially IServiceScopeFactory is the interface responsible for creating IServiceScope instances which are in turn responsible for managing the lifetime of IServiceProvider - which is the interface we use to resolve dependencies i.e. IServiceProvider. GetService(type) .
In Asp.Net Core a new service scope will be created at each request and will be disposed when request ended with a response or an exception. A scoped service is requested; Service scope will create an instance of the service that has not been already created in the service scope.
AddTransient() - This method creates a Transient service. A new instance of a Transient service is created each time it is requested. AddScoped() - This method creates a Scoped service. A new instance of a Scoped service is created once per request within the scope.
You registered the IEmailRepository
as a scoped service, in the Startup
class.
This means that you can not inject it as a constructor parameter in Middleware
because only Singleton
services can be resolved by constructor injection in Middleware
. You should move the dependency to the Invoke
method like this:
public ExceptionHandlingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context, IEmailRepository emailRepository)
{
try
{
await _next.Invoke(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex, emailRepository);
}
}
Another way to get the instance of scoped dependency is to inject service provider (IServiceProvider
) into the middleware constructor, create scope
in Invoke
method and then get the required service from the scope:
using (var scope = _serviceProvider.CreateScope()) {
var _emailRepository = scope.ServiceProvider.GetRequiredService<IEmailRepository>();
//do your stuff....
}
Check out Resolving Services in a Method Body in asp.net core dependency injection best practices tips tricks for more details.
Middleware is always a singleton so you can't have scoped dependencies as constructor dependencies in the constructor of your middleware.
Middleware supports method injection on the Invoke method,so you can just add the IEmailRepository emailRepository as a parameter to that method and it will be injected there and will be fine as scoped.
public async Task Invoke(HttpContext context, IEmailRepository emailRepository)
{
....
}
Your middleware
and the service
has to be compatible with each other in order to inject the service
via the constructor
of your middleware
. Here, your middleware
has been created as a convention-based middleware
which means it acts as a singleton service
and you have created your service as scoped-service
. So, you cannot inject a scoped-service
into the constructor of a singleton-service
because it forces the scoped-service
to act as a singleton
one. However, here are your options.
InvokeAsync
method.middleware
to a factory-based
one.A Factory-based middleware
is able to act as a scoped-service
. So, you can inject another scoped-service
via the constructor of that middleware. Below, I have shown you how to create a factory-based
middleware.
This is only for demonstration. So, I have removed all the other code.
public class Startup
{
public Startup()
{
}
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<TestMiddleware>();
services.AddScoped<TestService>();
}
public void Configure(IApplicationBuilder app)
{
app.UseMiddleware<TestMiddleware>();
}
}
The TestMiddleware
:
public class TestMiddleware : IMiddleware
{
public TestMiddleware(TestService testService)
{
}
public Task InvokeAsync(HttpContext context, RequestDelegate next)
{
return next.Invoke(context);
}
}
The TestService
:
public class TestService
{
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With