ASP.NET Core 2.2 下外掛化開發的改進
筆者在Asp.Net MVC 外掛化開發簡化方案 研究了基於 .NET Framework 的 ASP.NET 外掛化開發之後,又在ASP.NET Core 2.0 下進行外掛化開發 中研究了基於 ASP.NET Core 2.0 的外掛化開發,以及在 ASP.NET Core 2.0 中基於 Razor Page 的外掛化開發。不過最近在基於 .NET Core 2.1 的外掛化開發時遇到個新問題:
ASP.NET Core 2.1 可以將檢視編譯在動態庫中,生成一個proj.views.dll
這樣的動態庫,釋出時就不需要再發布Views
目錄了。然而使用上述外掛化方法,即使.views.dll
正確的拷貝到目標目錄,甚至 Shadow Copy 和 Assembly 載入都沒有問題的情況下,執行時仍然要在Views
目錄下去查詢檢視檔案。
雖然可以像 2.0 版本那樣拷貝Views
目錄達到正常執行的效果,但是既然拷貝所有.dll
就能解決的問題,誰還願意再去多拷貝一個Views
呢?
筆者查閱了大量資料之後,總算找到了問題的根源:.views.dll
不能採用預設的載入方式,而必須使用CompiledRazorAssemblyApplicationPartFactory
來載入。CompiledRazorAssemblyApplicationPartFactory
可以將 Assebmly 載入成ApplicationPart
,再新增到ApplicationPartManager
中去。因此,需要在IMvcBuilder.ConfigureApplicationPartManager()
中來配置處理(參閱:Stack Overflow 上的 ASP.NET Core MVC 2.1 mvc Views in plugin
)
不過外掛化框架中,為了解耦平臺和外掛,外掛 Assembly 是動態搜尋並載入的,並不能直接寫硬程式碼。這在之前的部落格中也曾提到,需要通過Startup
中Configure()
和ConfigureServices()
配合,並通過一個mvcBuilder
成員變數來處理。載入仍然要在LoadPlugins()
中進行(參閱:ASP.NET Core 2.0 下進行外掛化開發
),而ConfigureApplicationPartManager()
也需要搬到LoadPlugins()
中去:
private void LoadPlugins(IHostringEnvironment env) { // 這裡是之前進行 Shadow Copy 的程式碼 // ...... // 接下來需要把 dll 按是否 `.views.dll` 來分別處理 // 從 Shadow Copy 目錄載入 Assembly 並註冊到 Mvc 中 var groups = Directory.EnumerateFiles(target, "*.dll") .GroupBy(path => path.EndsWith(".views.dll", StringComparison.OrdinalIgnoreCase)) .ToDictionary(group => group.Key); // 非 .views.dll 直接載入到為 ApplicationPart groups[false] .Select(AssemblyLoadContext.Default.LoadFromAssemblyPath) .ForEach(mvcBuilder.AddApplicationPart); // .views.dll 需要通過 CompiledRazorAssemblyApplicationPartFactory 來載入 mvcBuilder.ConfigureApplicationPartManager(manager => { var razorPartFactory = new CompiledRazorAssemblyApplicationPartFactory(); groups[true] .Select(AssemblyLoadContext.Default.LoadFromAssemblyPath) .SelectMany(assembly => razorPartFactory.GetApplicationParts(assembly)) .ForEach(manager.ApplicationParts.Add); }); }
相關程式碼在 Gitee 上:aspnet-mvc-plugin-sample/asp.net_core_22