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