一、架构概述
在工业上位机软件开发中,PLC 通信协议种类繁多(Modbus、S7、Fins、ADS、OPC UA 等),且客户现场设备差异大。传统做法将所有协议硬编码进主程序,导致代码膨胀、发布周期长、新增协议必须重启软件。
基于 AssemblyLoadContext (ALC) 的热插拔插件架构,可实现:
协议插件独立部署:每个 PLC 协议封装为独立 DLL,放入插件目录自动加载
运行时动态插拔:新增、更新、移除协议插件无需重启主程序
依赖隔离:各插件使用独立依赖版本,互不冲突
统一编程模型:上层业务面向抽象接口编程,无需关心底层协议实现
二、核心技术原理
2.1 AssemblyLoadContext 机制
.NET Core 3.0+ 引入的 AssemblyLoadContext 是实现热插拔的基石。每个 ALC 拥有独立的程序集加载空间,设置 isCollectible: true 后支持完整卸载,释放内存并解除文件锁定。
关键特性:
隔离加载:插件与其依赖项在独立上下文中加载,避免版本冲突
可回收卸载:调用
Unload()后,GC 可回收整个上下文的所有程序集依赖解析:配合
AssemblyDependencyResolver自动解析 NuGet 依赖
2.2 架构分层设计

三、项目结构

四、核心源码实现
4.1 共享接口层(PlcProtocol.Abstractions)
IPlcDriver.cs - 驱动插件主接口

IPlcConnection.cs - 连接操作接口

PlcDataPoint.cs & PlcConnectionOptions.cs

PlcDriverMetadataAttribute.cs - 插件元数据标记

4.2 插件加载引擎(PlcPlugin.Host)
PluginLoadContext.cs - 自定义加载上下文

PluginLoader.cs - 插件加载管理器

PlcDriverFactory.cs - 工厂封装

4.3 PLC 协议插件示例
Modbus.Plugin 项目文件

关键配置:EnableDynamicLoading=true 自动复制所有依赖;ExcludeAssets=runtime 确保共享接口不从插件目录加载,避免类型不一致
ModbusPlcDriver.cs - Modbus 插件实现


4.4 Avalonia 主程序集成
App.axaml.cs - 初始化插件加载器

MainViewModel.cs - 视图模型绑定

MainWindow.axaml - 主界面

五、热插拔操作流程
5.1 新增协议插件(无需重启)
开发新协议插件项目,实现
IPlcDriver接口并标记[PlcDriver]编译生成 DLL 及所有依赖
将整个插件文件夹复制到主程序
Plugins目录FileSystemWatcher自动检测新文件,延迟 500ms 后加载UI 协议列表实时刷新,新协议立即可用
5.2 更新协议插件(无需重启)
覆盖插件目录下的 DLL 文件
文件监听触发 Changed 事件
自动卸载旧 ALC、释放资源、加载新版本
正在使用该协议的活动连接会断开,需重新建立
5.3 移除协议插件(无需重启)
删除插件目录
自动卸载对应 ALC
UI 协议列表移除该项
六、关键技术要点与最佳实践
6.1 共享程序集的处理
共享接口程序集 PlcProtocol.Abstractions必须由主程序上下文加载,插件侧设置 <ExcludeAssets>runtime</ExcludeAssets>。否则主程序与插件会加载两份接口类型,导致 is、as 运算符失效。
6.2 文件锁定问题
使用 LoadFromStream 而非 LoadFromAssemblyPath 加载插件 DLL,避免文件被 CLR 锁定,实现覆盖更新。但注意:
程序集 PDB 符号文件无法流式加载,调试时可能受限
依赖 DLL 仍可能被锁定,建议更新时整体替换插件目录
6.3 卸载的前提条件
ALC 可卸载的前提是没有任何对象引用该上下文中的类型实例。因此:
卸载前必须调用
Dispose释放所有连接确保 UI 不再持有插件创建的 ViewModel / View 引用
建议采用弱事件模式订阅插件事件
6.4 依赖版本隔离
不同插件可引用不同版本的 NuGet 包(如一个用 Modbus 2.x,一个用 3.x),ALC 独立解析各自依赖,互不干扰。主程序共享的接口程序集版本必须全局一致。
6.5 异常隔离
单个插件加载失败或运行时抛出异常,不应导致主程序崩溃。插件加载器需捕获所有异常并记录日志。
七、扩展方向
插件元数据清单:每个插件目录放置
plugin.json,描述名称、版本、依赖、作者等信息插件 UI 注入:插件可提供 Avalonia UserControl,动态注入主界面配置面板
插件依赖注入:支持将主程序的 ILogger、IConfiguration 等服务注入插件构造函数
安全沙箱:对第三方插件进行权限隔离,限制文件 IO、网络访问等
批量读写优化:上层统一合并读写请求,底层协议按需分包
这套架构已在工业上位机场景验证,支持十几种 PLC 协议的动态接入。新增协议时,开发人员只需专注于协议本身的实现,主程序零改动、零重启,大幅缩短了现场部署和迭代周期。
【源码】
关注评论或者回复【777】得:《Avalonia 热插拔插件架构,新增 PLC 通信协议无需重启主程序的详细实战资料和完整源码》

夜雨聆风