OrchardCore 如何動態(tài)加載模塊?
本文轉(zhuǎn)載自微信公眾號「JeffckyShare」,作者Jeffcky 。轉(zhuǎn)載本文請聯(lián)系JeffckyShare公眾號。
若對OrchardCore有所了解的童鞋應(yīng)該知道,OrchardCore本身定位于CMS系統(tǒng),同時整個架構(gòu)并非前后分離,采用MVC模式開發(fā),基于此,由于內(nèi)置需要預(yù)編譯視圖以及考慮其他等等原因,不支持動態(tài)加載模塊,本文給出我所想到的動態(tài)加載模塊方案
OrchardCore基本使用示例
OrchardCore采用包管理各個模塊,所以有自建NuGet,我們提前配置好OrchardCore程序包源
項目采用前后分離,所以我們創(chuàng)建WebAPi應(yīng)用程序,為支持模塊化開發(fā),如上圖下載模塊開發(fā)應(yīng)用程序包
緊著在Startup文件中,添加OrchardCore服務(wù)以及使用其中間件,如下圖
至此一個基本的模塊化項目就創(chuàng)建完畢,接下來我們創(chuàng)建模塊,官方提供模塊包模板引擎
通過對應(yīng)命令將模板引擎下載至本地
- dotnet new -i OrchardCore.ProjectTemplates::1.0.0-rc2-16113 --nuget-source https://nuget.cloudsmith.io/orchardcore/preview/v3/index.json
然后我們在項目解決方案下,繼續(xù)通過CLI將下載至本地模板引擎來創(chuàng)建模塊項目,并引入到項目解決方案中
- dotnet new ocmodulecms -n Test
由于我們用不到視圖,所以將視圖文件夾以及對應(yīng)默認安裝包刪除,只需保留如下模塊包就好
- OrchardCore.Module.Targets
同時也一并將項目文件中支持MVC配置給刪除,否則會生成視圖程序集,猜測應(yīng)該會引起模塊加載依賴需額外加載視圖dll
我們將模塊默認創(chuàng)建控制器修改為訪問接口形式,方便接下來測試驗證
那么接下來我們應(yīng)該如何將開發(fā)好的模塊進行加載呢?
OrchardCore動態(tài)加載模塊(前后分離)
了解OrchardCore基本原理的我們應(yīng)該知道,默認情況下,主項目添加模塊引用時,會通過MSBuild在對應(yīng)模塊程序集中,添加模塊標識,如下:
如上圖所示,一個是模塊標識,一個是對應(yīng)文件路徑標識
當啟動主項目時,會找到對應(yīng)程序集模塊標識,并注冊服務(wù)以及其他操作,如此看來,我們只需深入了解源碼中是否存在存儲對應(yīng)模塊信息的接口呢?
查看底層模塊設(shè)施源碼,得知對外暴露其接口即IModuleNamesProvider
好家伙,我們將生成模塊dll放在主項目程序啟動modules目錄下,接下來我們實現(xiàn)該接口,如下:
- public class DynamicModuleNamesProvider : IModuleNamesProvider
- {
- private readonly List<string> _moduleNames = new List<string>();
- public DynamicModuleNamesProvider()
- {
- var baseDirectory = AppContext.BaseDirectory;
- var location = Path.Combine(baseDirectory, "modules");
- if (!Directory.Exists(location))
- {
- return;
- }
- foreach (var file in Directory.EnumerateFiles(location))
- {
- var assemblyPath = Path.Combine(location, file);
- var assembly = Assembly.LoadFrom(assemblyPath);
- _moduleNames.AddRange(assembly.GetCustomAttributes<ModuleMarkerAttribute>().Select(m => m.Name));
- }
- }
- public IEnumerable<string> GetModuleNames()
- {
- return _moduleNames;
- }
- }
將其以單例形式注入,如下
- services.AddSingleton<IModuleNamesProvider, DynamicModuleNamesProvider>();
我們啟動主項目驗證確認,模塊已然進行加載,如下:
但是訪問控制器接口卻顯示404
并未繼續(xù)深入查看源碼,至少可知,通過動態(tài)加載內(nèi)置僅僅只注冊了相關(guān)服務(wù),猜測是和移除對應(yīng)視圖包有關(guān)導(dǎo)致并未激活控制器、視圖等等
OrchardCore動態(tài)加載模塊激活控制器
由于控制器、視圖、TagHelper等等相關(guān)FeatureProvider并未激活,所以我們借助AssemblyPart來實現(xiàn),將其作為應(yīng)用程序的一部分,通過掃描模塊,將對應(yīng)控制器等激活,如下:
- var builders = services.AddControllers();
- builders.ConfigureApplicationPartManager(apm =>
- {
- var baseDirectory = AppContext.BaseDirectory;
- var location = Path.Combine(baseDirectory, "modules");
- if (!Directory.Exists(location))
- {
- return;
- }
- foreach (var file in Directory.EnumerateFiles(location))
- {
- var assemblyPath = Path.Combine(location, file);
- var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);
- var assemblyPart = new AssemblyPart(assembly);
- apm.ApplicationParts.Add(assemblyPart);
- }
- });
雖然官方并未提供動態(tài)加載模塊示例,但我們依然可以借用其對外暴露接口來實現(xiàn),理論上若是采用MVC模式,應(yīng)該也可以進行動態(tài)加載