.NET 7中AssemblyLoadContext的LoadFromAssemblyPath无法后续卸载问题
这种差异是设计使然,但也和你使用PluginLoader的具体方式直接相关,以下是具体解析:
核心原因差异
CollectableAssemblyLoadContext(CALC)+ MemoryStream加载:
CALC的核心设计目标就是支持程序集卸载,通过MemoryStream加载时,程序集字节被完全加载到内存,与磁盘文件无绑定关系。只要你确保没有任何跨上下文的强引用(比如主应用上下文持有CALC中程序集的类型实例、静态引用等),CALC就能正确跟踪并释放所有相关资源,实现卸载。PluginLoader + LoadFromAssemblyPath加载:
如果你使用的PluginLoader是基于默认AssemblyLoadContext或者非可回收的自定义上下文加载程序集,那么抛出“不可回收程序集”异常是预期行为——因为默认上下文本身是不可回收的,一旦程序集加载到默认上下文,就无法被卸载。另外,即便你指定CALC作为加载上下文,
LoadFromAssemblyPath也有隐含限制:如果待加载程序集的依赖项已经存在于默认上下文(或其他不可回收上下文),或者加载后的程序集被外部上下文持有强引用,CALC会因为无法满足“无跨上下文引用”的卸载条件,同样触发该异常。
正确实现卸载的操作要点
如果想通过路径加载并实现卸载,必须满足以下条件:
- 为
PluginLoader指定自定义的CollectableAssemblyLoadContext,而非默认上下文。 - 确保待加载程序集的所有依赖项都完全加载到同一个CALC中,避免依赖默认上下文内的程序集(系统共享程序集除外,它们会被加载到共享上下文,不影响卸载)。
- 使用完程序集后,彻底释放所有对该程序集中类型、对象的强引用,确保没有引用泄漏到主应用上下文。
示例代码
// 创建可回收的加载上下文 var collectableContext = new CollectableAssemblyLoadContext(isCollectible: true); try { // 从路径加载程序集到可回收上下文 var pluginAssembly = collectableContext.LoadFromAssemblyPath(@"D:\MyWpfPlugins\TestPlugin.dll"); // 在这里执行插件逻辑,注意避免将插件内的对象留在主应用的强引用中 } finally { // 触发卸载 collectableContext.Unload(); }
总结
LoadFromAssemblyPath本身不限制程序集是否可回收,问题的核心在于承载它的AssemblyLoadContext是否为可回收类型,以及是否满足卸载的前置条件。你遇到的异常,要么是因为使用了不可回收的加载上下文,要么是存在跨上下文的强引用导致无法满足卸载要求。
内容的提问来源于stack exchange,提问作者Boppity Bop

