How to do DI in asp.net core middleware?
Solution 1
UserManager<ApplicationUser>
is (by default) registered as a scoped dependency, whereas your CreateCompanyMiddleware
middleware is constructed at app startup (effectively making it a singleton). This is a fairly standard error saying that you can't take a scoped dependency into a singleton class.
The fix is simple in this case - you can inject the UserManager<ApplicationUser>
into your Invoke
method:
public async Task Invoke(HttpContext context, UserManager<ApplicationUser> userManager)
{
await _next.Invoke(context);
}
This is documented in ASP.NET Core Middleware: Per-request middleware dependencies:
Because middleware is constructed at app startup, not per-request, scoped lifetime services used by middleware constructors aren't shared with other dependency-injected types during each request. If you must share a scoped service between your middleware and other types, add these services to the
Invoke
method's signature. TheInvoke
method can accept additional parameters that are populated by DI:
Solution 2
Another way to do that is to create a middleware by IMiddleware
interface and register it as a service
For example , the middleware
public class CreateCompanyMiddlewareByInterface : IMiddleware
{
private readonly UserManager<ApplicationUser> _userManager;
public CreateCompanyMiddlewareByInterface(UserManager<ApplicationUser> userManager )
{
this._userManager = userManager;
}
public Task InvokeAsync(HttpContext context, RequestDelegate next)
{
return next(context);
}
}
and service registeration :
services.AddScoped<CreateCompanyMiddlewareByInterface>();
- So why it happens ?
The middlewares using IMiddleware
are built by UseMiddlewareInterface(appBuilder, middlewareType type)
:
private static IApplicationBuilder UseMiddlewareInterface(IApplicationBuilder app, Type middlewareType)
{
return app.Use(next =>
{
return async context =>
{
var middlewareFactory = (IMiddlewareFactory)context.RequestServices.GetService(typeof(IMiddlewareFactory));
if (middlewareFactory == null) { /* throw ... */ }
var middleware = middlewareFactory.Create(middlewareType);
if (middleware == null) { /* throw ... */ }
try{
await middleware.InvokeAsync(context, next);
}
finally{
middlewareFactory.Release(middleware);
}
};
});
}
here the codes inside the context=>{}
are executed per-request . So every time there's an incoming request , the var middleware = middlewareFactory.Create(middlewareType);
will be executed and then ask for a middleware of middlewareType
( which is already registered as a service ) from the ServiceProvider
.
As for by-convention middlewares , there's no factory creating them .
Those instances are all created by ActivatorUtilities.CreateInstance()
at startup time . And any Invoke
method of by-convention middlewares , such as
Task Invoke(HttpContext context,UserManager<ApplicationUser> userManage, ILoggerFactory loggeryFactory , ... )
will be compiled into a function like below :
Task Invoke(Middleware instance, HttpContext httpContext, IServiceprovider provider)
{
var useManager /* = get service from service provider */ ;
var log = /* = get service from service provider */ ;
// ...
return instance.Invoke(httpContext,userManager,log, ...);
}
As you see , here the instance is created at startup time , and those services of Invoke
method are requested per request .
Comments
-
Yasser Shaikh almost 2 years
I am trying to inject dependency into my middleware constructor as follows
public class CreateCompanyMiddleware { private readonly RequestDelegate _next; private readonly UserManager<ApplicationUser> _userManager; public CreateCompanyMiddleware(RequestDelegate next , UserManager<ApplicationUser> userManager ) { _next = next; } public async Task Invoke(HttpContext context) { await _next.Invoke(context); } }
My Startup.cs file looks like
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<ApplicationDbContext>(options => options.UseMySql(Configuration.GetConnectionString("IdentityConnection"))); services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); ... app.UseMiddleware<CreateCompanyMiddleware>(); ...
But I am getting this error
An error occurred while starting the application. InvalidOperationException: Cannot resolve scoped service 'Microsoft.AspNetCore.Identity.UserManager`1[Common.Models.ApplicationUser]' from root provider. Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteValidator.ValidateResolution(Type serviceType, IServiceScope scope, IServiceScope rootScope)
-
th3morg about 4 yearsThe Factory approach is described here: docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/…
-
Fordy almost 3 yearsNice simple solution that worked for me. Note, if you forget to add the DI classes in the startup.cs then errors in the middleware won't always appear on screen and break points won't get hit. It can appear that the code is working, but it's probably not.