ASP.NET?Core?MVC創(chuàng)建控制器與依賴注入講解
默認(rèn)的IControllerActivator
在 ASP.NET Core 中,當(dāng) MVC 中間件接收到請求時(shí),通過路由選擇要執(zhí)行的控制器和操作方法。為了實(shí)際的執(zhí)行操作, MVC 中間件必須創(chuàng)建所選控制器的實(shí)例。
創(chuàng)建控制器的過程依賴眾多不同的提供者和工廠類,但最終是由實(shí)現(xiàn)IControllerActivator接口的實(shí)例來決定的。實(shí)現(xiàn)類只需要實(shí)現(xiàn)兩個方法:
public interface IControllerActivator
{
object Create(ControllerContext context);
void Release(ControllerContext context, object controller);
}如您所見,該IControllerActivator.Create方法傳遞了用于創(chuàng)建控制器的ControllerContext實(shí)例??刂破鞯膭?chuàng)建方式取決于具體的實(shí)現(xiàn)。
眾所周知,ASP.NET Core 使用的是DefaultControllerActivator,它通過TypeActivatorCache來創(chuàng)建控制器。TypeActivatorCache通過調(diào)用類的構(gòu)造函數(shù),并試圖從 DI 容器中解析構(gòu)造函數(shù)所需參數(shù)的實(shí)例。
有一點(diǎn)很重要,DefaultControllerActivator 不會試圖從 DI 容器中解析控制器的實(shí)例,只會解析控制器的依賴項(xiàng)。
DefaultControllerActivator 示例
為了演示這個行為,我創(chuàng)建了一個簡單的 MVC 應(yīng)用程序,包括一個單一的服務(wù)和一個控制器。服務(wù)實(shí)例有一個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的實(shí)例。將HomeController類型注冊到 DI 容器中,并且在TestService實(shí)例中傳遞自定義Name屬性。
如果您運(yùn)行應(yīng)用程序,您會看到什么結(jié)果?

您可以看到,該TestService.Name屬性使用的是默認(rèn)值,表示TestService實(shí)例是直接從 DI 容器中獲取的,直接忽略了創(chuàng)建HomeController的工廠方法。
這很容易理解,當(dāng)您通過DefaultControllerActivator創(chuàng)建控制器時(shí),它不會從DI容器中創(chuàng)建HomeController實(shí)例,只會解析構(gòu)造函數(shù)的依賴項(xiàng)。
大多數(shù)情況下,使用DefaultControllerActivator是一個不錯的選擇,但有時(shí)您可能希望直接通過 DI 容器來創(chuàng)建控制器,比如您希望使用具有攔截器或裝飾器等功能的第三方容器。
幸運(yùn)的是,MVC 框架包含了一個這樣的IControllerActivator實(shí)現(xiàn),并提供了一種非常方便的擴(kuò)展方法來啟用它。
ServiceBasedControllerActivator
如您所見,DefaultControllerActivator使用TypeActivatorCache來創(chuàng)建控制器,MVC還包括另一個實(shí)現(xiàn),稱為 ServiceBasedControllerActivator,它是直接從 DI 容器中獲取控制器。它的實(shí)現(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)用程序時(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();
}
}通過上面的代碼,點(diǎn)擊主頁將通過 DI 容器來創(chuàng)建一個控制器。由于我們已經(jīng)注冊了一個創(chuàng)建HomeController的工廠方法,我們自定義TestService配置將被保留,使用替換后的Name屬性:

AddControllersAsServices方法實(shí)現(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ù)雜的事情,您可以隨時(shí)實(shí)現(xiàn)自己IControllerActivator;不過我找不到任何理由,這兩點(diǎn)實(shí)現(xiàn)還不能滿足您的需求!
總結(jié)
- 默認(rèn)情況下,在ASP.NET Core MVC 中
IControllerActivator配置為DefaultControllerActivator。 DefaultControllerActivator使用TypeActivatorCache來創(chuàng)建控制器。它從 DI 容器加載構(gòu)造函數(shù)所需參數(shù)來創(chuàng)建控制器的實(shí)例。- 您也可以使用
ServiceBasedControllerActivator作替代方法,它直接從 DI 容器加載控制器。您可以在Startup.ConfigureServices方法中使用MvcBuilder的AddControllersAsServices()擴(kuò)展方法來配置此激活方式。
到此這篇關(guān)于ASP.NET Core MVC創(chuàng)建控制器與依賴注入的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Asp.net利用一般處理程序?qū)崿F(xiàn)文件下載功能
這篇文章主要介紹了Asp.net利用一般處理程序?qū)崿F(xiàn)文件下載功能,非常不錯,具有參考借鑒價(jià)值,需要的朋友可以參考下2017-07-07
ASP.NET實(shí)現(xiàn)多域名多網(wǎng)站共享Session值的方法
實(shí)現(xiàn)功能:可設(shè)置哪些站點(diǎn)可以共享Session值,這樣就防止別人利用這個去訪問,要想實(shí)現(xiàn)這個功能就必須得把Session值 放入數(shù)據(jù)庫中, 所有我們先在VS命令工具下注冊一個2011-11-11
asp.net下按鈕點(diǎn)擊后禁用的實(shí)現(xiàn)代碼
有時(shí)候?yàn)榱瞬蛔層脩暨B續(xù)的點(diǎn)擊某按鈕,我們會選擇將其在點(diǎn)擊后禁用。2010-09-09
ASP.NET中如何實(shí)現(xiàn)回調(diào)
這篇文章主要為大家詳細(xì)介紹了ASP.NET中如何實(shí)現(xiàn)回調(diào)操作,感興趣的小伙伴們可以參考一下2016-04-04
如何判斷?.NET?Core?應(yīng)用程序以管理員身份運(yùn)行的
這篇文章主要介紹了如何判斷?.NET?Core?應(yīng)用程序是以管理員身份運(yùn)行的,我們需要知道當(dāng)前程序是否以管理員身份運(yùn)行,以便執(zhí)行一些需要特殊權(quán)限的操作,下面為我們就來學(xué)習(xí)具體的方法吧,需要的朋友可以參考一下2022-03-03
ASP.NET MVC使用ActionFilterAttribute實(shí)現(xiàn)權(quán)限限制的方法(附demo源碼下載)
這篇文章主要介紹了ASP.NET MVC使用ActionFilterAttribute實(shí)現(xiàn)權(quán)限限制的方法,結(jié)合實(shí)例形式分析了ASP.NET MVC使用ActionFilterAttribute過濾類實(shí)現(xiàn)權(quán)限限制的步驟與相關(guān)技巧,并附帶demo源碼供讀者下載,需要的朋友可以參考下2016-04-04

