前言
今天,我們再次討論下OrchardCore,通過初期調研,我們專案採用OrchardCore底層設施支援模組化,同時根據業務場景,額外還需支援二次開發,於是有了本文,若有不同解決方案,歡迎留言探討
若對OrchardCore有所瞭解的童鞋應該知道,OrchardCore本身定位於CMS系統,同時整個架構並非前後分離,採用MVC模式開發,基於此,由於內建需要預編譯檢視以及考慮其他等等原因,不支援動態載入模組,本文給出我所想到的動態載入模組方案
OrchardCore基本使用示例
OrchardCore採用包管理各個模組,所以有自建NuGet,我們提前配置好OrchardCore程式包源
專案採用前後分離,所以我們建立WebAPi應用程式,為支援模組化開發,如上圖下載模組開發應用程式包,緊著在Startup檔案中,新增OrchardCore服務以及使用其中介軟體,如下圖
至此一個基本的模組化專案就建立完畢,接下來我們建立模組,官方提供模組包模板引擎
通過對應命令將模板引擎下載至本地
dotnet new -i OrchardCore.ProjectTemplates::1.0.0-rc2-16113 --nuget-source https://nuget.cloudsmith.io/orchardcore/preview/v3/index.json
然後我們在專案解決方案下,繼續通過CLI將下載至本地模板引擎來建立模組專案,並引入到專案解決方案中
dotnet new ocmodulecms -n Test
由於我們用不到檢視,所以將檢視資料夾以及對應預設安裝包刪除,只需保留模組包【OrchardCore.Module.Targets】就好,同時也一併將專案檔案中支援MVC配置給刪除,否則會生成檢視程式集,猜測應該會引起模組載入依賴需額外載入檢視dll
我們將模組預設建立控制器修改為訪問介面形式,方便接下來測試驗證
那麼接下來我們應該如何將開發好的模組進行載入呢?
OrchardCore動態載入模組(前後分離)
瞭解OrchardCore基本原理的我們應該知道,預設情況下,主專案新增模組引用時,會通過MSBuild在對應模組程式集中,新增模組標識,如下:
如上圖所示,一個是模組標識,一個是對應檔案路徑標識,當啟動主專案時,會找到對應程式集模組標識,並註冊服務以及其他操作,如此看來,我們只需深入瞭解原始碼中是否存在儲存對應模組資訊的介面呢?
檢視底層模組設施原始碼,得知對外暴露其介面即IModuleNamesProvider,我們將生成模組dll放在主專案程式啟動modules目錄下,接下來我們實現該介面,如下:
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
並未繼續深入檢視原始碼,至少可知,通過動態載入內建僅僅只註冊了相關服務,猜測是和移除對應檢視包有關導致並未啟用控制器、檢視等等
OrchardCore動態載入模組啟用控制器
由於控制器、檢視、TagHelper等等相關FeatureProvider並未啟用,所以我們藉助AssemblyPart來實現,將其作為應用程式的一部分,通過掃描模組,將對應控制器等啟用,如下:
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); } });
總結
雖然官方並未提供動態載入模組示例,但我們依然可以借用其對外暴露介面來實現,理論上若是採用MVC模式,應該也可以進行動態載入!