Skip to content

v0.2.5 更新日志

发布日期:2026 年 5 月 7 日

v0.2.5 是 v0.2.x 的修复与功能迭代版本,包含一处问题修复(插件 ALC 被 GC 提前回收)和一项增强功能(插件配置文件增量合并)。

v0.2.5 不包含任何破坏性变更,Contracts 未做改动,现有部署和插件无需任何修改即可升级。

修复内容

修复:插件 AssemblyLoadContext 被 GC 提前回收导致的服务注册失败

在特定条件下,插件加载后进入服务注册阶段(RegisterServices)时,会抛出 System.Reflection.ReflectionTypeLoadException 或插件类型无法解析的异常。

根因分析:

插件使用 isCollectible: trueAssemblyLoadContext(ALC)实现隔离加载。v0.2.4 及之前版本中,PluginLoader 实例是 ApplicationHost.RunAsync 方法的局部变量,方法执行完毕后失去所有强引用。当 GC 在服务注册阶段触发回收时,这些 ALC 会被标记为 unloading,导致从插件程序集加载类型失败。

修复方式:

  • PluginLoader 由局部变量提升为 ApplicationHost 的实例字段 _pluginLoader,在 RunAsync 的整个生命周期内持有强引用
  • PluginLoader 内部新增 _loadContexts 列表,追踪所有已创建的 PluginLoadContext 实例,确保在服务注册完成前 ALC 保持 alive 状态
  • 新增 UnloadAll() 方法,提供在主程序退出时清理所有 ALC 的入口
变更文件
_pluginLoader 实例字段ApplicationHost.cs
_loadContexts 追踪列表 + LoadContexts 属性 + UnloadAll()PluginLoader.cs

新增内容

新增:插件配置文件增量合并

v0.2.5 之前,EnsurePluginConfigFile 仅在插件配置文件不存在时从 DefaultConfig 生成默认配置。如果插件开发者在新版本中为 DefaultConfig 新增了配置键,现有用户的 config/{插件名}.json 永远不会感知这些新增的键,除非用户手动删除配置文件后重启。

v0.2.5 对配置文件的生成逻辑进行了全面增强:

旧行为:

配置文件是否存在?
  ├── 不存在 → 从 DefaultConfig 生成,写入
  └── 已存在 → 不做任何操作,即使 DefaultConfig 新增了键也不合并

新行为:

配置文件是否存在?
  ├── 不存在 → 从 DefaultConfig 生成,写入
  └── 已存在 → 读取现有配置,与 DefaultConfig 递归合并缺失的键,写回

合并规则如下:

场景行为
DefaultConfig 中的键在现有配置中不存在新增到现有配置中(使用 DefaultConfig 的键名大小写)
DefaultConfig 中的键在现有配置中已存在(大小写完全匹配)值类型或数组 → 保留现有值,不覆盖;双方都是嵌套对象 → 递归合并缺失的子键
DefaultConfig 中的键在现有配置中存在但大小写不同保留现有键名大小写,不重复添加;嵌套对象同样递归合并
现有配置中多余的键(DefaultConfig 不包含)保留,不删除

这一增强使得插件开发者可以在 DefaultConfig 中放心添加新配置项,用户的现有配置文件会在启动时自动增量补充,既不会覆盖已有的用户配置,也不会删除用户的额外配置项

csharp
// 示例:某插件 v1.0 的 DefaultConfig
public object? DefaultConfig => new MySettings { Host = "localhost" };
// 用户现有 config/my.plugin.json:{ "Host": "192.168.1.100" }

// 某插件 v1.1 的 DefaultConfig(新增 Port 字段)
public object? DefaultConfig => new MySettings { Host = "localhost", Port = 8080 };
// 升级后首次启动,config/my.plugin.json 自动变为:
// { "Host": "192.168.1.100", "Port": 8080 }
// ↑ Host 保留用户的值,Port 从 DefaultConfig 自动补充

兼容性说明

  • 无破坏性变更,直接替换主程序二进制文件即可完成升级
  • Contracts 未变更,插件无需重新编译,无需更新 NuGet 包引用
  • 配置文件增量合并在后台自动完成,无需用户或开发者手动介入
  • 如果插件开发者不希望新增的键被自动补充到用户配置中,应在 DefaultConfig 中保持该键为 null 或不包含该属性