首頁 > 軟體

ASP.NET Core MVC建立控制器與依賴注入講解

2022-02-21 13:05:40

預設的IControllerActivator

在 ASP.NET Core 中,當 MVC 中介軟體接收到請求時,通過路由選擇要執行的控制器和操作方法。為了實際的執行操作, MVC 中介軟體必須建立所選控制器的範例。

建立控制器的過程依賴眾多不同的提供者和工廠類,但最終是由實現IControllerActivator介面的範例來決定的。實現類只需要實現兩個方法:

public interface IControllerActivator  
{
    object Create(ControllerContext context);
    void Release(ControllerContext context, object controller);
}

如您所見,該IControllerActivator.Create方法傳遞了用於建立控制器的ControllerContext範例。控制器的建立方式取決於具體的實現。

眾所周知,ASP.NET Core 使用的是DefaultControllerActivator,它通過TypeActivatorCache來建立控制器。TypeActivatorCache通過呼叫類別建構函式,並試圖從 DI 容器中解解建構函式所需引數的範例。

有一點很重要,DefaultControllerActivator 不會試圖從 DI 容器中解析控制器的範例,只會解析控制器的依賴項。

DefaultControllerActivator 範例

為了演示這個行為,我建立了一個簡單的 MVC 應用程式,包括一個單一的服務和一個控制器。服務範例有一個name屬性,它通過建構函式來設定。預設情況下,它使用"default"作為預設值。

public class TestService  
{
    public TestService(string name = "default")
    {
        Name = name;
    }

    public string Name { get; }
}

在應用程式中HomeController依賴於TestService,並返回Name屬性的值:

public class HomeController : Controller  
{
    private readonly TestService _testService;
    public HomeController(TestService testService)
    {
        _testService = testService;
    }

    public string Index()
    {
        return "TestService.Name: " + _testService.Name;
    }
}

還有一塊程式碼在Startup檔案中。在這裡我將TestService註冊在 DI 容器中作為範圍內服務,並設定 MVC 中介軟體和服務:

public class Startup  
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();

        services.AddScoped<TestService>();
        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

如您所見,DefaultControllerActivator使用TypeActivatorCache來建立控制器,MVC還包括另一個實現,稱為 ServiceBasedControllerActivator,它是直接從 DI 容器中獲取控制器。它的實現非常簡單:

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<TestService>();
        services.AddTransient(ctx =>
            new HomeController(new TestService("Non-default value")));
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseMvcWithDefaultRoute();
    }
}

通過上面的程式碼,點選主頁將通過 DI 容器來建立一個控制器。由於我們已經註冊了一個建立HomeController的工廠方法,我們自定義TestService設定將被保留,使用替換後的Name屬性:

AddControllersAsServices方法實現了兩件事情 - 它將您應用程式中的所有控制器註冊到 DI 容器(如果尚未註冊),並將IControllerActivator註冊為ServiceBasedControllerActivator

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<IControllerActivator, ServiceBasedControllerActivator>());

    return builder;
}

如果需要做一些更復雜的事情,您可以隨時實現自己IControllerActivator;不過我找不到任何理由,這兩點實現還不能滿足您的需求!

總結

  • 預設情況下,在ASP.NET Core MVC 中IControllerActivator設定為DefaultControllerActivator
  • DefaultControllerActivator使用TypeActivatorCache來建立控制器。它從 DI 容器載入建構函式所需引數來建立控制器的範例。
  • 您也可以使用ServiceBasedControllerActivator作替代方法,它直接從 DI 容器載入控制器。您可以在Startup.ConfigureServices方法中使用MvcBuilderAddControllersAsServices()擴充套件方法來設定此啟用方式。

 到此這篇關於ASP.NET Core MVC建立控制器與依賴注入的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com