Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do DI in asp.net core middleware?

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)

like image 746
Yasser Shaikh Avatar asked Sep 06 '18 12:09

Yasser Shaikh


People also ask

How does DI work in .NET Core?

ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies. For more information specific to dependency injection within MVC controllers, see Dependency injection into controllers in ASP.NET Core.

What is dependency injection middle ware in ASP.NET Core?

ASP.NET Core - Dependency Injection. ASP.NET Core is designed from scratch to support Dependency Injection. ASP.NET Core injects objects of dependency classes through constructor or method by using built-in IoC container.

How do I use middleware in NET Core?

Middleware are software components that are assembled into an application pipeline to handle requests and responses. Each component chooses whether to pass the request on to the next component in the pipeline, and can perform certain actions before and after the next component is invoked in the pipeline.


2 Answers

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. The Invoke method can accept additional parameters that are populated by DI:

like image 74
Kirk Larkin Avatar answered Oct 02 '22 23:10

Kirk Larkin


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>(); 
  1. 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 .

like image 24
itminus Avatar answered Oct 03 '22 01:10

itminus