筆者在 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 的插件化開發時遇到個新問題:git
ASP.NET Core 2.1 能夠將視圖編譯在動態庫中,生成一個 proj.views.dll
這樣的動態庫,發佈時就不須要再發布 Views
目錄了。然而使用上述插件化方法,即便 .views.dll
正確的拷貝到目標目錄,甚至 Shadow Copy 和 Assembly 加載都沒有問題的狀況下,運行時仍然要在 Views
目錄下去查找視圖文件。segmentfault
雖然能夠像 2.0 版本那樣拷貝 Views
目錄達到正常運行的效果,可是既然拷貝全部 .dll
就能解決的問題,誰還願意再去多拷貝一個 Views
呢?mvc
筆者查閱了大量資料以後,總算找到了問題的根源:.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()
中去:asp.net
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_22ui