.NET Core 中插件式开发实现

在 .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 方法只是为了启动卸载。以下目标达成后,卸载完成:

1、没有线程将程序集中的方法加载到其调用堆栈上的 AssemblyLoadContext 中。

2、程序集中的任何类型都不会加载到 AssemblyLoadContext,这些类型的实例本身由以下引用:

    • AssemblyLoadContext 外部的引用,弱引用(WeakReference 或 WeakReference)除外。

    • 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://github.com/cwsheng/PluginsApp

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/132506.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Javaweb之HTML,CSS的详细解析

2.4 表格标签 场景&#xff1a;在网页中以表格&#xff08;行、列&#xff09;形式整齐展示数据&#xff0c;我们在一些管理类的系统中&#xff0c;会看到数据通常都是以表格的形式呈现出来的&#xff0c;比如&#xff1a;班级表、学生表、课程表、成绩表等等。 标签&#xff…

输电线路AR可视化巡检降低作业风险

随着现代工业的快速发展&#xff0c;各行业的一线技术工人要处理的问题越来越复杂&#xff0c;一些工作中棘手的问题迫切需要远端专家的协同处理。但远端专家赶来现场往往面临着专家差旅成本高、设备停机损失大、专业支持滞后、突发故障无法立即解决等痛点。传统的远程协助似乎…

OFDM同步--载波频率偏差CFO

参考书籍&#xff1a;《MIMO-OFDM无线通信技术及MATLAB实现》 实验图基本都截取自该本书 一、什么是CFO OFDM解调是采用同步检波的方式&#xff0c;需要在接收机使用与发射机相同的载波信号进行向下变换恢复出基带信号。但在实际使用中无法获得完全相同的载波信号&#xff0c;…

Mysql之多表查询上篇

Mysql之多表查询上篇 多表查询什么是多表查询笛卡尔积(交叉连接)产生笛卡尔积的条件避免笛卡尔积的方法 多表查询的分类1.等值连接 VS 非等值连接等值连接非等值连接扩展1表的别名扩展2&#xff1a;连接多个表 2.自连接与非自连接扩展3&#xff1a;SQL语法标准 内连接SQL92语法…

03 贝尔曼公式

贝尔曼公式 前言1、Motivating examples2、state value3、Bellman equation:Derivation4、Bellman equation:Matrix-vector form4、Bellman equation:Solve the state value5、Action value 前言 本文来自西湖大学赵世钰老师的B站视频。本节课主要介绍贝尔曼公式。 本节课概要…

Jmeter之JSR223

一、JSR223组件 JSR是Java Specification Requests的缩写,意思是Java规范提案。JSR已成为Java界的一个重要标准. JSR223其实包含了有好几种组件,但是其用法都是一致的,并且都是执行一段代码&#xff0c;主要分类如下&#xff1a; JSR223 PreProcessor JSR223 Timer JSR223 S…

LeetCode热题100——链表

链表 1. 相交链表2. 反转链表3. 回文链表4. 环形链表5. 合并两个有序链表 1. 相交链表 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 // 题解&#xff1a;使用A/B循环遍…

最新ChatGPT商业运营系统源码+支持GPT4/支持ai绘画+支持Midjourney绘画

一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如…

WebSocket Day03 : SpringMVC整合WebSocket

前言 在现代Web应用程序中&#xff0c;实时性和即时通信变得越来越重要。传统的HTTP请求-响应模式无法满足实时数据传输和双向通信的需求。随着技术的发展&#xff0c;WebSocket成为了一种强大而灵活的解决方案。 WebSocket是HTML5提供的一种新的通信协议&#xff0c;它通过一…

JavaEE的渊源

JavaEE的渊源 1. JavaEE的起源2. JavaEE与Spring的诞生3. JavaEE发展历程&#xff08;2003-2007&#xff09;4. JavaEE发展历程&#xff08;2009-至今&#xff09;5. Java的Spec数目与网络结构 1. JavaEE的起源 我们首先来讲一下JavaEE的起源 ,为什么要来讲起源 &#xff1f; …

Unit1_3:分治算法之排序问题

文章目录 一、归并排序二、快速排序思路伪代码流程图时间复杂度改进 三、堆排序结构插入提取最小值排序抽象 四、比较排序总结决策树模型 一、归并排序 归并排序子操作的思路和Unit1_2逆序计算一样 下面写一下伪代码 if left < right thencenter←L(left right)/2];Merge…

使用 Clipdrop 替换长安三万里电影海报中的天空

长安三万里是一部不久前上映的古装动画电影&#xff0c;讲述了李白和高适的故事。电影海报中的天空是一片晴朗的月空&#xff0c;与扬州城的景色相得益彰。 最近&#xff0c;我发现了一款名为 Clipdrop 的软件&#xff0c;可以用来替换图片中的天空。这款软件使用人工智能技术&…

MyBatis缓存详解

1. MyBatis缓存 MyBatis中的缓存是用来提高性能&#xff0c;减少数据库交互次数的机制。它分为一级缓存&#xff08;Local Cache&#xff09;和二级缓存&#xff08;Global Cache&#xff09;。 1.1 一级缓存&#xff08;Local Cache&#xff09; 作用范围&#xff1a;一级缓…

深入了解汽车级功率MOSFET NVMFS2D3P04M8LT1G P沟道数据表

汽车级功率MOSFET是一种专门用于汽车电子领域的功率MOSFET。它具有高电压、高电流、高温、高可靠性等特点&#xff0c;能够满足汽车电子领域对功率器件的严格要求。汽车级功率MOSFET广泛应用于汽车电机驱动、泵电机控制、车身控制等方面&#xff0c;能够提高汽车电子系统的效率…

Flume从入门到精通一站式学习笔记

文章目录 什么是FlumeFlume的特性Flume高级应用场景Flume的三大核心组件Source&#xff1a;数据源channelsink Flume安装部署Flume的使用案例&#xff1a;采集文件内容上传至HDFS案例&#xff1a;采集网站日志上传至HDFS 各种自定义组件例如&#xff1a;自定义source例如&#…

【RtpSeqNumOnlyRefFinder】webrtc m98: ManageFrameInternal 的帧决策过程分析

Jitterbuffer(FrameBuffer)需要组帧以后GOP内的参考关系 JeffreyLau 大神分析 了组帧原理而参考关系(RtpFrameReferenceFinder)的生成伴随了帧决策 FrameDecisionFrameDecision 影响力 帧的缓存。调用 OnAssembledFrame 传递已经拿到的RtpFrameObject 那么,RtpFrameObject…

安全性在外卖系统开发中的重要性

外卖系统的开发需要强调安全性&#xff0c;因为系统中涉及用户的个人信息、支付信息以及交易数据。确保这些信息的安全对于用户信任和系统的成功至关重要。以下是在外卖系统开发中提升安全性的一些建议。 数据加密 用户信息和支付数据应该经过加密处理。对于敏感信息&#x…

计算机的主存储器与辅助存储器

文章目录 前言一、主存储器&#xff08;内存&#xff09;1.主存储器特征2.主存储器构成3.主存储器和CPU如何交互4.主存储器和操作系统位数的关系 二、辅助存储器&#xff08;磁盘&#xff09;1.辅助存储器构成2.辅助存储器特征3.磁盘的调度算法3.1先来先服务算法3.2最短寻道时间…

错误:ERROR Cannot read properties of null (reading ‘type‘)

ERROR Cannot read properties of null (reading ‘type’) TypeError: Cannot read properties of null (reading ‘type’) <template><el-card><el-row :gutter"20" class"header"><el-col :span"7"><el-input pl…