.NET Core中插件式开发实现
前言:
之前在文章- AppDomain实现【插件式】开发 中介绍了在 .NET Framework 中,通过AppDomain实现动态加载和卸载程序集的效果。
但是.NET Core 仅支持单个默认应用域,那么在.NET Core中如何实现【插件式】开发呢?
一、.NET Core 中 AssemblyLoadContext的使用
1、AssemblyLoadContext简介:
每个 .NET Core 应用程序均隐式使用 AssemblyLoadContext。 它是运行时的提供程序,用于定位和加载依赖项。 只要加载了依赖项,就会调用 AssemblyLoadContext 实例来定位该依赖项。
它提供定位、加载和缓存托管程序集和其他依赖项的服务。
为了支持动态代码加载和卸载,它创建了一个独立上下文,用于在其自己的 AssemblyLoadContext 实例中加载代码及其依赖项。
2、AssemblyLoadContext和AppDomain卸载差异:
使用 AssemblyLoadContext 和使用 AppDomain 进行卸载之间存在一个值得注意的差异。 对于 Appdomain,卸载为强制执行。
卸载时,会中止目标 AppDomain 中运行的所有线程,会销毁目标 AppDomain 中创建的托管 COM 对象,等等。 对于 AssemblyLoadContext,卸载是“协作式的”。
调用 AssemblyLoadContext.Unload 方法只是为了启动卸载。以下目标达成后,卸载完成:
- 没有线程将程序集中的方法加载到其调用堆栈上的
AssemblyLoadContext中。 - 程序集中的任何类型都不会加载到
AssemblyLoadContext,这些类型的实例本身由以下引用: AssemblyLoadContext外部的引用,弱引用(WeakReference 或 WeakReference<T>)除外。AssemblyLoadContext内部和外部的强垃圾回收器 (GC) 句柄(GCHandleType.Normal 或 GCHandleType.Pinned)。
二、.NET Core 插件式方式实现
1、创建可卸载的上下文PluginAssemblyLoadContext
class PluginAssemblyLoadContext : AssemblyLoadContext
{
private AssemblyDependencyResolver _resolver; /// <summary>
/// 构造函数
/// isCollectible: true 重点,允许Unload
/// </summary>
/// <param name="pluginPath"></param>
public PluginAssemblyLoadContext(string pluginPath) : base(isCollectible: true)
{
_resolver = new AssemblyDependencyResolver(pluginPath);
} protected override Assembly Load(AssemblyName assemblyName)
{
string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
if (assemblyPath != null)
{
return LoadFromAssemblyPath(assemblyPath);
}
return null;
} protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
{
string libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
if (libraryPath != null)
{
return LoadUnmanagedDllFromPath(libraryPath);
}
return IntPtr.Zero;
}
}
2、创建插件接口及实现
整体项目结构为:

a)添加项目PluginInterface,插件接口:
public interface IPlugin
{
string Name { get; }
string Description { get; }
string Execute(object inPars);
}
b)添加HelloPlugin项目,实现不引用外部dll插件
public class HelloPlugin : PluginInterface.IPlugin
{
public string Name => "HelloPlugin";
public string Description { get => "Displays hello message."; }
public string Execute(object inPars)
{return ("Hello !!!" + inPars?.ToString());
}
}
c)添加JsonPlugin项目,实现引用三方dll插件
public class JsonPlugin : PluginInterface.IPlugin
{
public string Name => "JsonPlugin";
public string Description => "Outputs JSON value.";
private struct Info
{
public string JsonVersion;
public string JsonLocation;
public string Machine;
public DateTime Date;
}
public string Execute(object inPars)
{
Assembly jsonAssembly = typeof(JsonConvert).Assembly;
Info info = new Info()
{
JsonVersion = jsonAssembly.FullName,
JsonLocation = jsonAssembly.Location,
Machine = Environment.MachineName,
Date = DateTime.Now
};
return JsonConvert.SerializeObject(info, Formatting.Indented);
}
}
d)添加PluginsApp项目,实现调用插件方法:
修改窗体界面布局:

添加执行方法
/// <summary>
/// 将此方法标记为noinline很重要,否则JIT可能会决定将其内联到Main方法中。
/// 这可能会阻止成功卸载插件,因为某些实例的生存期可能会延长到预期卸载插件的时间点之外。
/// </summary>
/// <param name="assemblyPath"></param>
/// <param name="inPars"></param>
/// <param name="alcWeakRef"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.NoInlining)]
static string ExecuteAndUnload(string assemblyPath, object inPars, out WeakReference alcWeakRef)
{
string resultString = string.Empty;
// 创建 PluginLoadContext对象
var alc = new PluginAssemblyLoadContext(assemblyPath); //创建一个对AssemblyLoadContext的弱引用,允许我们检测卸载何时完成
alcWeakRef = new WeakReference(alc); // 加载程序到上下文
// 注意:路径必须为绝对路径.
Assembly assembly = alc.LoadFromAssemblyPath(assemblyPath); //创建插件对象并调用
foreach (Type type in assembly.GetTypes())
{
if (typeof(IPlugin).IsAssignableFrom(type))
{
IPlugin result = Activator.CreateInstance(type) as IPlugin;
if (result != null)
{
resultString = result.Execute(inPars);
break;
}
}
}
//卸载程序集上下文
alc.Unload();
return resultString;
}
三、效果验证
1、非引用外部dll的插件执行:执行后对应dll成功卸载,程序集数量未增加。

2、引用外部包的插件:执行后对应dll未卸载,程序集数量增加。

通过监视查看对象状态:该上下文在卸载中。暂未找到原因卸载失败(疑问?)

四、总结:
虽然微软文档说.Net Core中使用AssemblyLoadContext来实现程序集的加载及卸载实现,但通过验证在加载引用外部dll后,加载后不能正常卸载。或者使用方式还不正确。
参考:https://docs.microsoft.com/zh-cn/dotnet/standard/assembly/unloadability
.NET Core中插件式开发实现的更多相关文章
- 零基础ASP.NET Core MVC插件式开发
零基础ASP.NET Core MVC插件式开发 一个项目随着业务模块的不断增加,系统会越来越庞大.如果参与开发的人员越多,管理起来也难度也很大.面对这样的情况,首先想到的是模块化插件式开发,根据业务 ...
- 从零开始实现ASP.NET Core MVC的插件式开发(一) - 使用ApplicationPart动态加载控制器和视图
标题:从零开始实现ASP.NET Core MVC的插件式开发(一) - 使用Application Part动态加载控制器和视图 作者:Lamond Lu 地址:http://www.cnblogs ...
- 从零开始实现ASP.NET Core MVC的插件式开发(二) - 如何创建项目模板
标题:从零开始实现ASP.NET Core MVC的插件式开发(二) - 如何创建项目模板 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p/11155 ...
- 从零开始实现ASP.NET Core MVC的插件式开发(三) - 如何在运行时启用组件
标题:从零开始实现ASP.NET Core MVC的插件式开发(三) - 如何在运行时启用组件 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p/112 ...
- 从零开始实现ASP.NET Core MVC的插件式开发(四) - 插件安装
标题:从零开始实现ASP.NET Core MVC的插件式开发(四) - 插件安装 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p/11260750. ...
- 从零开始实现ASP.NET Core MVC的插件式开发(五) - 插件的删除和升级
标题:从零开始实现ASP.NET Core MVC的插件式开发(五) - 使用AssemblyLoadContext实现插件的升级和删除 作者:Lamond Lu 地址:https://www.cnb ...
- 从零开始实现ASP.NET Core MVC的插件式开发(六) - 如何加载插件引用
标题:从零开始实现ASP.NET Core MVC的插件式开发(六) - 如何加载插件引用. 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p/1171 ...
- 从零开始实现ASP.NET Core MVC的插件式开发(七) - 近期问题汇总及部分解决方案
标题:从零开始实现ASP.NET Core MVC的插件式开发(七) - 问题汇总及部分解决方案 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p/12 ...
- 从零开始实现ASP.NET Core MVC的插件式开发(八) - Razor视图相关问题及解决方案
标题:从零开始实现ASP.NET Core MVC的插件式开发(八) - Razor视图相关问题及解决方案 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun ...
随机推荐
- Ambassador-05-自动重试
自动重试定义: retry_policy: retry_on: <string> num_retries: <integer> per_try_timeout: <str ...
- flex 的 三个参数 flex:1 0 auto
flex :flex-group flex-shirk flex-basis ①.flex-group 剩余空间索取 默认值为0,不索取 eg:父元素400,子元素A为100px,B为200px. ...
- Laravel artisan 命令
获取命令列表 php artisan Laravel Framework 7.26.0 Usage: command [options] [arguments] Options: -h, --help ...
- 安全之路 —— 无DLL文件实现远程进程注入
简介 在之前的章节中,笔者曾介绍过有关于远程线程注入的知识,将后门.dll文件注入explorer.exe中实现绕过防火墙反弹后门.但一个.exe文件总要在注入时捎上一个.dll文件着实是怪麻烦的,那 ...
- 数据库的读写分离(Amoeba)
目录 Amoeba Amoeba读写分离的配置 Amoeba Amoeba(变形虫) 项目,该开源框架于2008年开始发布一款 Amoeba for Mysql软件. 这个软件基于Java致力于MyS ...
- 如何以最简单的方式安装 KALI 渗透测试框架系统
0x01 第一步下载 KALI 百度搜索 KALI 官网,找到下载区,我选的是 64 位标准版,但是推荐下载 32 位(功能貌似更全) 这个为下载后的 iso 镜像文件 0x02 第二步打开虚拟机,配 ...
- java8中的Optional
Optional类主要解决空指针异常NullPointerException.Optional 类(java.util.Optional)是一个容器类,代表一个值存在或不存在,原来用 null 表示一 ...
- 3 Java概述
java三大版本 javase:标准版(桌面程序,控制台开发) javame:嵌入式开发(手机,家电)目前陨落 javaee:企业级开发(web端..) JDK和JRE 定义 JDK是开发工具包 Jr ...
- Git 无法添加文件夹下的文件
尚未暂存以备提交的变更: (使用 "git add <文件>..." 更新要提交的内容) (使用 "git checkout -- <文件>... ...
- Linux Limit相关内容设置大全(值得收藏)
目录 一. /etc/security/limits.conf 详解 /etc/security/limits.conf 配置解析 /etc/security/limits.d/目录 二. ulimi ...