ASP.NET Core MVCコントローラ作成と依存注入

6789 ワード

私の最後のASPについてNET CoreリリースIDsiposableオブジェクトの文書(中国語、英語原文)では、Mark RendleはMVCコントローラがリクエスト終了時にもリソースをリリースすると指摘している.一見、この範囲内のリソースがリクエスト終了時に解放されるのは明らかであるようだが、MVCコントローラの処理方式は実際には多くのサービスとは少し異なる.
この記事では、ASPについてご紹介します.NET Core MVCのIControllerActivatorがコントローラをどのように作成したのか、および依存注入によってコントローラを作成したのかの違い.

デフォルトのIControllerActivator


ASP.NET Coreでは、MVCミドルウェアがリクエストを受信すると、実行するコントローラと操作方法をルーティングで選択します.実際に動作するためには、MVCミドルウェアは、選択したコントローラのインスタンスを作成する必要があります.
コントローラを作成するプロセスは、多くの異なるプロバイダおよびファクトリクラスに依存するが、最終的にはIControllerActivatorインタフェースを実装するインスタンスによって決定される.実装クラスには2つの方法しか必要ありません.
public interface IControllerActivator  {    object Create(ControllerContext context);    void Release(ControllerContext context, object controller);
}

ご覧のように、IControllerActivator.Createメソッドは、コントローラを作成するためのControllerContextのインスタンスを渡します.コントローラの作成方法は、特定のインプリメンテーションに依存します.
周知の通り、ASP.NET Coreは、TypeActivatvatorCacheによってコントローラを作成するDefaultControllerActivatorを使用しています.TypeActivatorCacheクラスのコンストラクション関数を呼び出し、DIコンテナからコンストラクション関数に必要なパラメータの例を解析しようとする.
重要な点は、DefaultControllerActivatorは、コントローラのインスタンスをDIコンテナから解析しようとせず、コントローラの依存項目のみを解析することです.

DefaultControllerActivatvatorの例


この動作を実証するために,単一のサービスとコントローラを含む簡単なMVCアプリケーションを作成した.サービスインスタンスには、関数を構築することによって設定されるnameプロパティがあります.デフォルトでは、"default"がデフォルトとして使用されます.
public class TestService  {    public TestService(string name = "default")    {
        Name = name;
    }    public string Name { get; }
}

アプリケーションでは、HomeControllerTestServiceに依存し、Nameのプロパティの値を返します.
public class HomeController : Controller  {    private readonly TestService _testService;    public HomeController(TestService testService)    {
        _testService = testService;
    }    public string Index()    {        return "TestService.Name: " + _testService.Name;
    }
}
Startupファイルにもう1つのコードがあります.ここで私はTestServiceをDI容器に登録して範囲内のサービスとして、MVCミドルウェアとサービスを設定します.
public class Startup  {    public void ConfigureServices(IServiceCollection services)    {
        services.AddMvc();

        services.AddScoped();
        services.AddTransient(ctx =>            new HomeController(new TestService("Non-default value")));
    }    public void Configure(IApplicationBuilder app)    {
        app.UseMvcWithDefaultRoute();
    }
}
HomeControllerのインスタンスを作成するためのファクトリメソッドを定義したことに気づきます.HomeControllerタイプがDIコンテナに登録され、TestServiceインスタンスにおいてカスタムName属性が渡される.
アプリケーションを実行すると、どのような結果が得られますか?
このTestService.Nameプロパティはデフォルト値を使用しており、TestServiceインスタンスがDIコンテナから直接取得されたことを示しており、HomeControllerを作成するファクトリメソッドは直接無視されています.
これは、コントローラをDefaultControllerActivatorで作成した場合、DIコンテナからHomeControllerインスタンスは作成されず、コンストラクション関数の依存性のみが解析されることを容易に理解できます.
ほとんどの場合、DefaultControllerActivatorを使用するのは良い選択ですが、遮断器や装飾器などの機能を持つサードパーティ製コンテナを使用するなど、DIコンテナを直接使用してコントローラを作成したい場合があります.
幸いなことに、MVCフレームワークはこのようなIControllerActivator実装を含み、それを有効にするために非常に便利な拡張方法を提供している.

ServiceBasedControllerActivator


ご覧のように、DefaultControllerActivatorTypeActivatorCacheを使用してコントローラを作成します.MVCには、DIコンテナから直接コントローラを取得するServiceBasedControllerActivatorと呼ばれる別のインプリメンテーションも含まれています.その実現は非常に簡単です.
public class ServiceBasedControllerActivator : IControllerActivator  {    public object Create(ControllerContext actionContext)    {        var controllerType = actionContext.ActionDescriptor.ControllerTypeInfo.AsType();        return actionContext.HttpContext.RequestServices.GetRequiredService(controllerType);
    }    public virtual void Release(ControllerContext context, object controller)    {
    }
}

アプリケーションにMVCサービスを追加する場合は、AddControllersAsServices()拡張メソッドを使用してDIベースのアクターを構成できます.
public class Startup  {    public void ConfigureServices(IServiceCollection services)    {
        services.AddMvc()
                .AddControllersAsServices();

        services.AddScoped();
        services.AddTransient(ctx =>            new HomeController(new TestService("Non-default value")));
    }    public void Configure(IApplicationBuilder app)    {
        app.UseMvcWithDefaultRoute();
    }
}

上のコードで、ホームページをクリックするとDIコンテナでコントローラが作成されます.HomeControllerを作成するファクトリメソッドを登録したため、カスタムTestServiceの構成は保持され、置き換えられたNameのプロパティを使用します.AddControllersAsServicesメソッドは、アプリケーション内のすべてのコントローラをDIコンテナに登録し(登録されていない場合)、IControllerActivatorServiceBasedControllerActivatorに登録します.
public static IMvcBuilder AddControllersAsServices(this IMvcBuilder builder)  
{    var feature = new ControllerFeature();
    builder.PartManager.PopulateFeature(feature);    foreach (var controller in feature.Controllers.Select(c => c.AsType()))
    {
        builder.Services.TryAddTransient(controller, controller);
    }

    builder.Services.Replace(ServiceDescriptor.Transient());    return builder;
}

もっと複雑なことをする必要がある場合は、いつでも自分のIControllerActivatorを実現することができます.しかし、私は何の理由も見つかりません.この2つの実現はまだあなたのニーズを満たすことができません.

まとめ

  • デフォルトでは、ASP.NET Core MVCではIControllerActivatorDefaultControllerActivatorに構成されている.
  • DefaultControllerActivatorコントローラを作成するには、TypeActivatorCacheを使用します.DIコンテナからコンストラクション関数に必要なパラメータをロードして、コントローラのインスタンスを作成します.
  • コントローラをDIコンテナから直接ロードする代わりに、ServiceBasedControllerActivatorを使用することもできます.Startup.ConfigureServicesメソッドでは、MvcBuilderAddControllersAsServices()拡張メソッドを使用して、このアクティブ化メソッドを構成できます.