ASP.NET Core MVC創建控制器與依賴註入講解
默認的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
方法中使用MvcBuilder
的AddControllersAsServices()
擴展方法來配置此激活方式。
到此這篇關於ASP.NET Core MVC創建控制器與依賴註入的文章就介紹到這瞭。希望對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。
推薦閱讀:
- .Net Core中使用Autofac替換自帶的DI容器的示例
- 在ASP.Net Core中使用Lamar的全過程
- .NET Core使用Autofac容器的DI依賴註入,IOC控制反轉及AOP切面編程
- .NET中IoC框架Autofac用法講解
- ASP.NET Core 依賴註入詳細