Как сделать 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>();
  1. Так почему же это происходит?

Промежуточное программное обеспечение, использующее 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 запрашиваются для каждого запроса.