ASP.NET?Core?MVC創(chuàng)建控制器與依賴注入講解
默認(rèn)的IControllerActivator
在 ASP.NET Core 中,當(dāng) MVC 中間件接收到請求時,通過路由選擇要執(zhí)行的控制器和操作方法。為了實際的執(zhí)行操作, MVC 中間件必須創(chuàng)建所選控制器的實例。
創(chuàng)建控制器的過程依賴眾多不同的提供者和工廠類,但最終是由實現(xiàn)IControllerActivator
接口的實例來決定的。實現(xiàn)類只需要實現(xiàn)兩個方法:
public interface IControllerActivator { object Create(ControllerContext context); void Release(ControllerContext context, object controller); }
如您所見,該IControllerActivator.Create
方法傳遞了用于創(chuàng)建控制器的ControllerContext
實例??刂破鞯膭?chuàng)建方式取決于具體的實現(xiàn)。
眾所周知,ASP.NET Core 使用的是DefaultControllerActivator
,它通過TypeActivatorCache來創(chuàng)建控制器。TypeActivatorCache
通過調(diào)用類的構(gòu)造函數(shù),并試圖從 DI 容器中解析構(gòu)造函數(shù)所需參數(shù)的實例。
有一點很重要,DefaultControllerActivator
不會試圖從 DI 容器中解析控制器的實例,只會解析控制器的依賴項。
DefaultControllerActivator 示例
為了演示這個行為,我創(chuàng)建了一個簡單的 MVC 應(yīng)用程序,包括一個單一的服務(wù)和一個控制器。服務(wù)實例有一個name屬性,它通過構(gòu)造函數(shù)來設(shè)置。默認(rèn)情況下,它使用"default"
作為默認(rèn)值。
public class TestService { public TestService(string name = "default") { Name = name; } public string Name { get; } }
在應(yīng)用程序中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 容器中作為范圍內(nèi)服務(wù),并設(shè)置 MVC 中間件和服務(wù):
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(); } }
您會注意到,我定義了一個工廠方法用于創(chuàng)建HomeController
的實例。將HomeController
類型注冊到 DI 容器中,并且在TestService
實例中傳遞自定義Name
屬性。
如果您運(yùn)行應(yīng)用程序,您會看到什么結(jié)果?
您可以看到,該TestService.Name
屬性使用的是默認(rèn)值,表示TestService
實例是直接從 DI 容器中獲取的,直接忽略了創(chuàng)建HomeController
的工廠方法。
這很容易理解,當(dāng)您通過DefaultControllerActivator
創(chuàng)建控制器時,它不會從DI容器中創(chuàng)建HomeController
實例,只會解析構(gòu)造函數(shù)的依賴項。
大多數(shù)情況下,使用DefaultControllerActivator
是一個不錯的選擇,但有時您可能希望直接通過 DI 容器來創(chuàng)建控制器,比如您希望使用具有攔截器或裝飾器等功能的第三方容器。
幸運(yùn)的是,MVC 框架包含了一個這樣的IControllerActivator
實現(xiàn),并提供了一種非常方便的擴(kuò)展方法來啟用它。
ServiceBasedControllerActivator
如您所見,DefaultControllerActivator
使用TypeActivatorCache
來創(chuàng)建控制器,MVC還包括另一個實現(xiàn),稱為 ServiceBasedControllerActivator
,它是直接從 DI 容器中獲取控制器。它的實現(xiàn)非常簡單:
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) { } }
當(dāng)您將 MVC 服務(wù)添加到應(yīng)用程序時,可以使用AddControllersAsServices()
擴(kuò)展方法配置基于 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 容器來創(chuàng)建一個控制器。由于我們已經(jīng)注冊了一個創(chuàng)建HomeController
的工廠方法,我們自定義TestService
配置將被保留,使用替換后的Name
屬性:
AddControllersAsServices
方法實現(xiàn)了兩件事情 - 它將您應(yīng)用程序中的所有控制器注冊到 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; }
如果需要做一些更復(fù)雜的事情,您可以隨時實現(xiàn)自己IControllerActivator
;不過我找不到任何理由,這兩點實現(xiàn)還不能滿足您的需求!
總結(jié)
- 默認(rèn)情況下,在ASP.NET Core MVC 中
IControllerActivator
配置為DefaultControllerActivator
。 DefaultControllerActivator
使用TypeActivatorCache
來創(chuàng)建控制器。它從 DI 容器加載構(gòu)造函數(shù)所需參數(shù)來創(chuàng)建控制器的實例。- 您也可以使用
ServiceBasedControllerActivator
作替代方法,它直接從 DI 容器加載控制器。您可以在Startup.ConfigureServices
方法中使用MvcBuilder
的AddControllersAsServices()
擴(kuò)展方法來配置此激活方式。
到此這篇關(guān)于ASP.NET Core MVC創(chuàng)建控制器與依賴注入的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
ASP.NET泛型三之使用協(xié)變和逆變實現(xiàn)類型轉(zhuǎn)換
這篇文章介紹了ASP.NET使用協(xié)變和逆變實現(xiàn)泛型類型轉(zhuǎn)換的方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-08-08[c#]asp.ent下開發(fā)中Tag的開發(fā)技巧
[c#]asp.ent下開發(fā)中Tag的開發(fā)技巧...2007-05-05TrieTree服務(wù)-組件構(gòu)成及其作用介紹
本文將一步步教你配置和使用TrieTree服務(wù),需要的朋友可以參考下2013-01-01ASP.NET與MySQL數(shù)據(jù)庫簡明圖示入門教程
ASP.NET與MySQL數(shù)據(jù)庫簡明圖示入門教程...2006-09-09ASP.NET MVC5 實現(xiàn)分頁查詢的示例代碼
本篇文章主要介紹了ASP.NET MVC5 實現(xiàn)分頁查詢的示例代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-02-02ASP.NET自帶對象JSON字符串與實體類的轉(zhuǎn)換
這篇文章主要介紹了ASP.NET自帶對象JSON字符串與實體類的轉(zhuǎn)換,感興趣的小伙伴們可以參考一下2016-07-07ASP.NET連接 Access數(shù)據(jù)庫的幾種方法
這篇文章主要介紹了ASP.NET連接 Access數(shù)據(jù)庫的幾種方法,每種方法都非常不錯,具有參考借鑒價值,需要的朋友一起學(xué)習(xí)吧2016-08-08ASP.NET中Response.BufferOutput屬性的使用技巧
這篇文章介紹了ASP.NET中Response.BufferOutput屬性的使用技巧,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-07-07