笔者在 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