Как сделать DI в промежуточном программном обеспечении asp.net?
Я пытаюсь ввести зависимость в свой конструктор промежуточного ПО следующим образом
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);
}
}
Мой файл Startup.cs выглядит так:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseMySql(Configuration.GetConnectionString("IdentityConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
...
app.UseMiddleware<CreateCompanyMiddleware>();
...
Но я получаю эту ошибку
Произошла ошибка при запуске приложения. InvalidOperationException: не удается разрешить область действия службы "Microsoft.AspNetCore.Identity.UserManager'1 [Common.Models.ApplicationUser]" от корневого провайдера. Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteValidator.ValidateResolution (тип serviceType, область IServiceScope, IServiceScope rootScope)
Ответы
Ответ 1
UserManager<ApplicationUser>
(по умолчанию) регистрируется как зависимая область, в то время как ваше промежуточное ПО CreateCompanyMiddleware
создается при запуске приложения (фактически делает его одноэлементным). Это довольно стандартная ошибка, говорящая о том, что вы не можете поместить зависимую область в одноэлементный класс.
В этом случае исправить просто - вы можете добавить UserManager<ApplicationUser>
в ваш метод Invoke
:
public async Task Invoke(HttpContext context, UserManager<ApplicationUser> userManager)
{
await _next.Invoke(context);
}
Это описано в Базовом промежуточном программном обеспечении ASP.NET: зависимости промежуточного программного обеспечения по запросу:
Поскольку промежуточное программное обеспечение создается при запуске приложения, а не по запросу, службы времени жизни с областью действия, используемые конструкторами промежуточного программного обеспечения, не используются совместно с другими типами, внедренными зависимостями, во время каждого запроса. Если вам необходимо совместно использовать сервис с определенной областью между промежуточным программным обеспечением и другими типами, добавьте эти сервисы в сигнатуру метода Invoke
. Метод Invoke
может принимать дополнительные параметры, которые заполняются DI:
Ответ 2
Еще один способ сделать это - создать промежуточное программное обеспечение с помощью интерфейса IMiddleware
и зарегистрировать его в качестве службы.
Например, промежуточное ПО
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);
}
}
и регистрация службы:
services.AddScoped<CreateCompanyMiddlewareByInterface>();
- Так почему же это происходит?
Промежуточное программное обеспечение, использующее IMiddleware
, создано 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);
}
};
});
}
здесь коды внутри context=>{}
выполняются по запросу. Поэтому каждый раз при поступлении входящего запроса выполняется var middleware = middlewareFactory.Create(middlewareType);
, а затем запрашивается промежуточное программное обеспечение middlewareType
(которое уже зарегистрировано как услуга) из ServiceProvider
.
Что касается промежуточного программного обеспечения, то, по общему мнению, фабрика его не создает.
Все эти экземпляры создаются ActivatorUtilities.CreateInstance()
во время запуска. И любой метод Invoke
промежуточного программного обеспечения по соглашению, такой как
Task Invoke(HttpContext context,UserManager<ApplicationUser> userManage, ILoggerFactory loggeryFactory , ... )
будет скомпилирован в функцию, как показано ниже:
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, ...);
}
Как видите, здесь экземпляр создается во время запуска, и эти сервисы метода Invoke
запрашиваются для каждого запроса.