web-dev-qa-db-ja.com

asp.netコアミドルウェアでDIを行う方法は?

次のようにミドルウェアコンストラクターに依存関係を注入しようとしています

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(Type serviceType、IServiceScope scope、IServiceScope rootScope)

26
Yasser Shaikh

UserManager<ApplicationUser>は(デフォルトで)scoped依存関係として登録されますが、CreateCompanyMiddlewareミドルウェアはアプリの起動時に構築されます(事実上、singleton)。これは、scoped依存関係をsingletonクラスに含めることができないことを示すかなり標準的なエラーです。

この場合、修正は簡単です-UserManager<ApplicationUser>Invokeメソッドに追加します。

public async Task Invoke(HttpContext context, UserManager<ApplicationUser> userManager)
{
    await _next.Invoke(context);
}

これは ASP.NET Core Middleware:Per-requestミドルウェアの依存関係 に文書化されています:

ミドルウェアはリクエストごとではなくアプリの起動時に構築されるため、ミドルウェアコンストラクターによって使用されるscopedライフタイムサービスは、リクエストごとに他の依存関係が挿入された型と共有されません。 scopedサービスをミドルウェアと他のタイプの間で共有する必要がある場合は、これらのサービスをInvokeメソッドのシグネチャに追加します。 Invokeメソッドは、DIによって入力される追加のパラメーターを受け入れることができます。

58
Kirk Larkin

これを行う別の方法は、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メソッドのサービスはリクエストごとにリクエストされます。

11
itminus