标题:从零开始实现ASP.NET Core MVC的插件式开发(六) - 如何加载插件引用。
作者:Lamond Lu
地址:https://www.cnblogs.com/lwqlun/p/11717254.html
源代码:https://github.com/lamondlu/DynamicPlugins
从零开始实现ASP.NET Core MVC的插件式开发(一) - 使用Application Part动态加载控制器和视图
从零开始实现ASP.NET Core MVC的插件式开发(二) - 如何创建项目模板
从零开始实现ASP.NET Core MVC的插件式开发(三) - 如何在运行时启用组件
从零开始实现ASP.NET Core MVC的插件式开发(四) - 插件安装
从零开始实现ASP.NET Core MVC的插件式开发(五) - 使用AssemblyLoadContext实现插件的升级和删除
简介在前一篇中,我给大家演示了如何使用.NET Core 3.0中新引入的AssemblyLoadContext来实现运行时升级和删除插件。完成此篇之后,我的得到了很多园友的反馈,很高兴有这么多人能够参与进来,我会根据大家的反馈,来完善这个项目。本篇呢,我将主要解决加载插件引用的问题,这个也是反馈中被问的最多的问题。
问题用例在之前做的插件中,我们做的都是非常非常简单的功能,没有引入任何的第三方库。但是正常情况下,我们所创建的插件或多或少的都会引用一些第三方库,那么下面我们来尝试一下,使用我们先前的项目,加载一个使用第三方程序集, 看看会的得到什么结果。
这里为了模拟,我创建了一个新的类库项目DemoReferenceLibrary, 并在之前的DemoPlugin1项目中引用DemoReferenceLibrary项目。
在DemoReferenceLibrary中,我新建了一个类Demo.cs文件, 其代码如下:
public class Demo { public string SayHello() { return "Hello World. Version 1"; } }这里就是简单的通过SayHello方法,返回了一个字符串。
然后在DemoPlugin1项目中,我们修改之前创建的Plugin1Controller,从Demo类中通过SayHello方法得到需要在页面中显示的字符串。
[Area("DemoPlugin1")] public class Plugin1Controller : Controller { public IActionResult HelloWorld() { var content = new Demo().SayHello(); ViewBag.Content = content; return View(); } }最后我们打包一下插件,重新将其安装到系统中,访问插件路由之后,就会得到以下错误。
这里就是大部分同学遇到的问题,无法加载程序集DemoReferenceLibrary。
如何加载插件引用?这个问题的原因很简单,就是当通过AssemblyLoadContext加载程序集的时候,我们只加载了插件程序集,没有加载它引用的程序集。
例如,我们以DemoPlugin1的为例,在这个插件的目录如下
在这个目录中,除了我们熟知的DemoPlugin1.dll,DemoPlugin1.Views.dll之外,还有一个DemoReferenceLibrary.dll文件。 这个文件我们并没有在插件启用时加载到当前的AssemblyLoadContext中,所以在访问插件路由时,系统找不到这个组件的dll文件。
为什么Mystique.Core.dll、System.Data.SqlClient.dll、Newtonsoft.Json.dll这些DLL不会出现问题呢?在.NET Core中有2种LoadContext。 一种是我们之前介绍的AssemblyLoadContext, 它是一种自定义LoadContext。 另外一种就是系统默认的DefaultLoadContext。当一个.NET Core应用启动的时候,都会创建并引用一个DefaultLoadContext。
如果没有指定LoadContext, 系统默认会将程序集都加载到DefaultLoadContext中。这里我们可以查看一下我们的主站点项目,这个项目我们也引用了Mystique.Core.dll、System.Data.SqlClient.dll、Newtonsoft.Json.dll。
在.NET Core的设计文档中,对于程序集加载有这样一段描述
If the assembly was already present in A1's context, either because we had successfully loaded it earlier, or because we failed to load it for some reason, we return the corresponding status (and assembly reference for the success case).
However, if C1 was not found in A1's context, the Load method override in A1's context is invoked.
For Custom LoadContext, this override is an opportunity to load an assembly before the fallback (see below) to Default LoadContext is attempted to resolve the load.
For Default LoadContext, this override always returns null since Default Context cannot override itself.