.NET Core 中插件式开发实现

news/2024/5/19 20:57:18 标签: c#, .netcore, 后端, 架构

在 .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.niftyadmin.cn/n/5146732.html

相关文章

FBM232 P0926GW 一个基于PC的Studio应用程序

FBM232 P0926GW 一个基于PC的Studio应用程序 告别自定义编程&#xff0c;向S88 Builder问好。它可以帮助您轻松地将泵、混合器和阀门等单个批处理设备配置为特定的协调任务&#xff0c;如灌装、加热和混合。 S88 Builder是什么&#xff1f;它包括一个基于PC的Studio应用程序&…

分享74个Python管理系统源代码总有一个是你想要的

分享74个Python管理系统源代码总有一个是你想要的 源码链接&#xff1a;https://pan.baidu.com/s/1JXFePOOk_ot4Jdu6_RylOg?pwd8888 提取码&#xff1a;8888 项目名称 ais系统后台项目&#xff0c;基于python flask框架 BNUZ教务系统认证爬虫Python语言实现&#xff0c;你…

OpenCV图像识别技术-验证码识别之前置基础

目录 一、opencv基础操作 二、修改图像像素与色彩 三、修改图像regionofinterest指定区域 四、图像数值运算、二值化处理 五、图像平滑处理 六、形态学图像处理 七、滑块验证码的基础&#xff1a;模板匹配 一、opencv基础操作 1.安装opencv&#xff1a;终端&#xff1a…

uniapp新建的vuecli项目启动报错并且打包失败的问题(已解决)

我的项目新建流程如下 运行之后就是如下报错 解决办法&#xff1a; 安装如下依赖&#xff1a; npm i postcss-loader autoprefixer8.0.0 npm run build 编译失败 安装如下依赖&#xff1a; npm install postcss8.2.2 最终package.json文件如下 {"name": "ls…

pca954x系列芯片对比(pca9543,pca9544,pca9545,pca9546,pca9547,pca9548)

pca954x系列芯片对比 本文主要内容: 1 pca9543 通用描述,原理框架,PDF下载连接 2 pca9544 通用描述,原理框架,PDF下载连接 3 pca9545 通用描述,原理框架,PDF下载连接 4 pca9546 通用描述,原理框架,PDF下载连接 5 pca9547 通用描述,原理框架,PDF下载连接 6 pca9548 通…

MapStruct 使用教程

在 Spring Boot 项目中使用 MapStruct&#xff0c;可以简化对象之间的映射操作&#xff0c;而不必手动编写大量的映射代码。以下是在 Spring Boot 项目中使用 MapStruct 的一般步骤&#xff1a; 添加依赖&#xff1a;首先&#xff0c;确保在你的 Spring Boot 项目的 pom.xml 文…

数仓建模—数据中台概论

文章目录 数据中台概论一、什么是中台二、什么是数据中台三、数据中台解决什么问题四、数据中台和数据仓库、数据平台的区别五、业务中台和数据中台的关系六、数据中台为何如此重要1. 回归服务的本质2. 数据中台需要持续的业务滋养3. 数据中台是培育业务创新的土壤4. 数据中台是…

深度开发者故事|API Explorer 助力教育行业 一站式搭建实验环境

华为云API Explorer为开发者提供一站式API解决方案统一平台&#xff0c;集成华为云服务所有开放API&#xff0c;支持全量快速检索、可视化调试、帮助文档、代码示例等能力&#xff0c;帮助开发者快速学习API&#xff0c;提升API开发效率。 产品链接&#xff1a;https://apiexp…