标题:从零开始实现ASP.NET Core MVC的插件式开发(八) - Razor视图相关问题及解决方案
作者:Lamond Lu
地址:https://www.cnblogs.com/lwqlun/p/13197683.html
源代码:https://github.com/lamondlu/Mystique
前景回顾
- 从零开始实现ASP.NET Core MVC的插件式开发(一) - 使用Application Part动态加载控制器和视图
- 从零开始实现ASP.NET Core MVC的插件式开发(二) - 如何创建项目模板
- 从零开始实现ASP.NET Core MVC的插件式开发(三) - 如何在运行时启用组件
- 从零开始实现ASP.NET Core MVC的插件式开发(四) - 插件安装
- 从零开始实现ASP.NET Core MVC的插件式开发(五) - 使用AssemblyLoadContext实现插件的升级和删除
- 从零开始实现ASP.NET Core MVC的插件式开发(六) - 如何加载插件引用
- 从零开始实现ASP.NET Core MVC的插件式开发(七) - 近期问题汇总及部分解决方案
简介
在上一篇中,我给大家分享了程序调试问题的解决方案以及如何实现插件中的消息传递,完稿之后,又收到了不少问题反馈,其中最严重的问题应该就是运行时编译Razor视图失败的问题。
本篇我就给大家分享一下我针对此问题的解决方案,最后还会补上上一篇中鸽掉的动态加载菜单(T.T)。
Razor视图中引用出错问题
为了模拟一下当前的问题,我们首先之前的插件1中添加一个新类TestClass
, 并在HelloWorld
方法中创建一个TestClass
对象作为视图模型传递给Razor视图,并在Razor视图中展示出TestClass
的Message
属性。
- TestClass.cs
public class TestClass
{
public string Message { get; set; }
}
- HelloWorld.cshtml
@using DemoPlugin1.Models;
@model TestClass
@{
}
<h1>@ViewBag.Content</h1>
<h2>@Model.Message</h2>
- Plugin1Controller.cs
[Area("DemoPlugin1")]
public class Plugin1Controller : Controller
{
private INotificationRegister _notificationRegister;
public Plugin1Controller(INotificationRegister notificationRegister)
{
_notificationRegister = notificationRegister;
}
[HttpGet]
public IActionResult HelloWorld()
{
string content = new Demo().SayHello();
ViewBag.Content = content + "; Plugin2 triggered";
TestClass testClass = new TestClass();
testClass.Message = "Hello World";
_notificationRegister.Publish("LoadHelloWorldEvent", JsonConvert.SerializeObject(new LoadHelloWorldEvent() { Str = "Hello World" }));
return View(testClass);
}
}
这个代码看似很简单,也是最常用的MVC视图展示方式,但是集成在动态组件系统中之后,你就会得到以下错误界面。
这里看起来似乎依然感觉是AssemblyLoadContext
的问题。主要的线索是,如果你将插件1的程序集直接引入主程序工程中,重新启动项目之后,此处代码能够正常访问,所以我猜想Razor
视图才进行运行时编译的时候,使用了默认的AssemblyLoadContext
,而非插件AssemblyPart
所在的AssemblyLoadContext
。
由此我做了一个实验,我在MystiqueSetup
方法中,在插件加载的时候,也向默认AssemblyLoadContext
中加载了插件程序集
public static void MystiqueSetup(this IServiceCollection services,
IConfiguration configuration)
{
...
using (IServiceScope scope = provider.CreateScope())
{
MvcRazorRuntimeCompilationOptions option = scope.ServiceProvider.GetService<MvcRazorRuntimeCompilationOptions>();
IUnitOfWork unitOfWork = scope.ServiceProvider.GetService<IUnitOfWork>();
List<ViewModels.PluginListItemViewModel> allEnabledPlugins = unitOfWork.PluginRepository.GetAllEnabledPlugins();
IReferenceLoader loader = scope.ServiceProvider.GetService<IReferenceLoader>();
foreach (ViewModels.PluginListItemViewModel plugin in allEnabledPlugins)
{
...
using (FileStream fs = new FileStream(filePath, FileMode.Open))
{
System.Reflection.Assembly assembly = context.LoadFromStream(fs);
context.SetEntryPoint(assembly);
loader.LoadStreamsIntoContext(context, referenceFolderPath, assembly);
...
fs.Position = 0;
AssemblyLoadContext.Default.LoadFromStream(fs);
}
context.Enable();
}
}
...
}
重新运行程序,访问插件1的路由,你就会得到以下错误。