深入解析ASP.NET Core MVC的模塊化設(shè)計(jì)[下篇]
ASP.NET Core MVC的“模塊化”設(shè)計(jì)使我們可以構(gòu)成應(yīng)用的基本單元Controller定義在任意的模塊(程序集)中,并在運(yùn)行時(shí)動(dòng)態(tài)加載和卸載。《設(shè)計(jì)篇》介紹了這種為“飛行中的飛機(jī)加油”的方案的實(shí)現(xiàn)原理?本篇我們將演示將介紹“分散定義Controller”的N種實(shí)現(xiàn)方案。源代碼從這里下載。
一、標(biāo)注ApplicationPartAttribute特性
二、標(biāo)注RelatedAssemblyAttribute特性
三、注冊(cè)ApplicationPartManager
四、添加ApplicationPart到現(xiàn)有ApplicationPartManager
一、標(biāo)注ApplicationPartAttribute特性
接下來(lái)我們就通過(guò)幾個(gè)簡(jiǎn)單的實(shí)例來(lái)演示如何將Controller類(lèi)型定義在非入口應(yīng)用所在的項(xiàng)目中。我們創(chuàng)建如圖1所示的解決方案,其中App是一個(gè)MVC應(yīng)用類(lèi)型的項(xiàng)目,而Foo則是一個(gè)普通的類(lèi)庫(kù)項(xiàng)目,App具有針對(duì)Foo的項(xiàng)目引用。我們希望將部分Controller類(lèi)型定義在Foo這個(gè)類(lèi)庫(kù)項(xiàng)目中。
![image_thumb[12] image_thumb[12]](https://img2023.cnblogs.com/blog/19327/202403/19327-20240304181018070-342110481.png)
圖1 將部分Controller類(lèi)型定義在Foo項(xiàng)目中
我們?cè)贏pp項(xiàng)目中定義了如下這個(gè)HomeController。如代碼片段所示,我們?cè)跇?gòu)造函數(shù)中注入了ApplicationPartManager對(duì)象,并利用它得到當(dāng)前應(yīng)用范圍內(nèi)所有有效Controller類(lèi)型。在執(zhí)行應(yīng)用根路徑的Action方法Index中,我們將得到的有效Controller類(lèi)型名稱(chēng)呈現(xiàn)出來(lái)。如下所示的FooController類(lèi)型是我們?cè)贔oo項(xiàng)目中定義的Controller類(lèi)型。
public class HomeController : Controller { private readonly IEnumerable<Type> _controllerTypes; public HomeController(ApplicationPartManager manager) { var feature = new ControllerFeature(); manager.PopulateFeature(feature); _controllerTypes = feature.Controllers; } [HttpGet("/")] public string Index() { var lines = _controllerTypes.Select(it => it.Name); return string.Join(Environment.NewLine, lines.ToArray()); } }
public class FooController { public void Index() => throw new NotImplementedException(); }
在啟動(dòng)這個(gè)演示程序后,如果利用瀏覽器通過(guò)根路徑訪問(wèn)定義在HomeController類(lèi)型中的Action方法Index,我們會(huì)得到如圖2所示的輸出結(jié)果。從輸出結(jié)果可以看出,定義在非MVC應(yīng)用項(xiàng)目Foo中的Controller類(lèi)型在默認(rèn)情況下是不會(huì)被解析的。
![image_thumb[14] image_thumb[14]](https://img2023.cnblogs.com/blog/19327/202403/19327-20240304181018902-1965838098.png)
圖2 默認(rèn)只解析MVC應(yīng)用所在項(xiàng)目定義的Controller
如果希望MVC應(yīng)用在進(jìn)行Controller類(lèi)型解析的時(shí)候?qū)㈨?xiàng)目Foo編譯后的程序集(默認(rèn)為Foo.dll)包括進(jìn)來(lái),我們可以在應(yīng)用所在項(xiàng)目中標(biāo)注ApplicationPartAttribute特性將程序集Foo作為應(yīng)用的組成部分。所以我們?cè)赑rogram.cs中針對(duì)ApplicationPartAttribute特性進(jìn)行了如下的標(biāo)記。
[assembly:ApplicationPart("Foo")]修改后的程序集啟動(dòng)之后,再次利用瀏覽器按照按照相同的路徑對(duì)它發(fā)起請(qǐng)求,我們將得到如圖3所示的輸出結(jié)果。由于程序集Foo成為了當(dāng)前應(yīng)用的有效組成部分,定義在它里面的BarController自然也成為了當(dāng)前應(yīng)用有效的Controller類(lèi)型。
![image_thumb[16] image_thumb[16]](https://img2023.cnblogs.com/blog/19327/202403/19327-20240304181019761-723506032.png)
圖3 解析ApplicationPartAttribute特性指向程序集中的Controller類(lèi)型
二、標(biāo)注RelatedAssemblyAttribute特性
除了在入口程序集上標(biāo)注ApplicationPartAttribute特性將某個(gè)程序集作為當(dāng)前應(yīng)用的有效組成部分之外,我們也可以通過(guò)標(biāo)注RelatedAssemblyAttribute達(dá)到相同的目的。根據(jù)前面的介紹,我們知道RelatedAssemblyAttribute特性只能標(biāo)注到入口程序集或者ApplicationPartAttribute特性指向的程序集中,所以我們可以將RelatedAssemblyAttribute特性標(biāo)注到Foo項(xiàng)目中將另一個(gè)程序集包含進(jìn)行。為此我們?cè)诮鉀Q方案中添加了另一個(gè)類(lèi)庫(kù)項(xiàng)目Bar(如圖4所示),并為App添加針對(duì)Bar的項(xiàng)目引用,然后在Bar項(xiàng)目中定義一個(gè)類(lèi)似于FooController的BarController類(lèi)型。
![image_thumb[19] image_thumb[19]](https://img2023.cnblogs.com/blog/19327/202403/19327-20240304181020702-52279697.png)
圖4 將部分Controller類(lèi)型定義在Foo和Bar項(xiàng)目中
為了將項(xiàng)目Bar編譯后生成的程序集(默認(rèn)為Bar.dll)作為當(dāng)前應(yīng)用的組成部分,我們可以選擇在App或者Foo項(xiàng)目中標(biāo)注一個(gè)指向它的RelatedAssemblyAttribute特性。對(duì)于我們演示的實(shí)例來(lái)說(shuō),我們選擇在FooController.cs中以如下形式標(biāo)注一個(gè)指向程序集Bar的RelatedAssemblyAttribute特性。
[assembly: RelatedAssembly("Bar")]修改后的程序集啟動(dòng)之后,再次利用瀏覽器按照按照相同的路徑對(duì)它發(fā)起請(qǐng)求,我們將得到如圖5所示的輸出結(jié)果。由于程序集Bar成為了當(dāng)前應(yīng)用的有效組成部分,定義在它里面的BazController自然也成為了當(dāng)前應(yīng)用有效的Controller類(lèi)型。
![image_thumb[21] image_thumb[21]](https://img2023.cnblogs.com/blog/19327/202403/19327-20240304181021480-1557368953.png)
圖5 解析RelatedAssemblyAttribute特性指向程序集中的Controller類(lèi)型
三、注冊(cè)ApplicationPartManager
由于針對(duì)有效Controller類(lèi)型的解析是利用注冊(cè)的ApplicationPartManager對(duì)象實(shí)現(xiàn)的,所以我們完全可以通過(guò)注冊(cè)一個(gè)ApplicationPartManager對(duì)象的方式達(dá)到相同的目的。接下來(lái)我們將上一個(gè)演示實(shí)例中標(biāo)注的ApplicationPartAttribute和RelatedAssemblyAttribute特性刪除,并將承載程序修改為如下的形式。
var manager = new ApplicationPartManager(); var entry = Assembly.GetEntryAssembly()!; var foo = Assembly.Load(new AssemblyName("Foo")); var bar = Assembly.Load(new AssemblyName("Bar")); manager.ApplicationParts.Add(new AssemblyPart(entry)); manager.ApplicationParts.Add(new AssemblyPart(foo)); manager.ApplicationParts.Add(new AssemblyPart(bar)); manager.FeatureProviders.Add(new ControllerFeatureProvider()); var builder = WebApplication.CreateBuilder(args); builder.Services .AddSingleton(manager) .AddControllers(); var app = builder.Build(); app.MapControllers(); app.Run();
如上面的代碼片段所示,我們創(chuàng)建了一個(gè)ApplicationPartManager對(duì)象,并在其ApplicationParts屬性中顯式添加了指向入口程序集以及Foo和Bar程序集的AssemblyPart對(duì)象。為了能夠讓這個(gè)ApplicationPartManager對(duì)象具有解析Controller類(lèi)型的能力,我們?cè)谄銯eatureProviders中添加了一個(gè)ControllerFeatureProvider對(duì)象。在后續(xù)的應(yīng)用承載程序中,我們將這個(gè)ApplicationPartManager對(duì)象作為服務(wù)注冊(cè)到依賴(lài)注入框架中。修改后的程序集啟動(dòng)之后,再次利用瀏覽器按照按照相同的路徑對(duì)它發(fā)起請(qǐng)求,我們依然會(huì)得到如圖5所示的輸出結(jié)果。
四、添加ApplicationPart到現(xiàn)有ApplicationPartManager
其實(shí)我們沒(méi)有必要注冊(cè)一個(gè)新的,按照如下的方式將Foo、Bar程序集轉(zhuǎn)換成AssemblyPart并將其添加到現(xiàn)有的ApplicationPartManager之中也可以達(dá)到相同的目的。
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddControllers()
.AddApplicationPart(Assembly.Load(new AssemblyName("Foo")))
.AddApplicationPart(Assembly.Load(new AssemblyName("Bar")));
var app = builder.Build();
app.MapControllers();
app.Run();深入解析ASP.NET Core MVC的模塊化設(shè)計(jì)[上篇]
深入解析ASP.NET Core MVC的模塊化設(shè)計(jì)[下篇]
如何實(shí)現(xiàn)運(yùn)行時(shí)動(dòng)態(tài)定義Controller類(lèi)型?


ASP.NET Core MVC的“模塊化”設(shè)計(jì)使我們可以構(gòu)成應(yīng)用的基本單元Controller定義在任意的模塊(程序集)中,并在運(yùn)行時(shí)動(dòng)態(tài)加載和卸載。《設(shè)計(jì)篇》介紹了這種為“飛行中的飛機(jī)加油”的方案的實(shí)現(xiàn)原理?本篇我們將演示將介紹“分散定義Controller”的N種實(shí)現(xiàn)方案。源代碼從這里下載。
浙公網(wǎng)安備 33010602011771號(hào)