.NET类库作插件调用时Newtonsoft.Json程序集加载失败如何解决
你遇到的"找不到指定程序集"报错,本质不是Newtonsoft.Json.dll文件物理缺失,而是.NET Framework的程序集绑定规则在插件宿主环境下不满足加载要求,和你本地清理引用、重配Nuget包的操作没有关系,核心触发原因有两类:
- 探测路径匹配失败:.NET Framework默认只会从宿主程序exe的根目录做依赖探测,不会主动扫描插件所在的子目录,哪怕你把所有dll都完整放到插件目录,系统也找不到。
- 强名称绑定校验不通过:编译时写入的程序集清单会记录引用的Newtonsoft.Json的AssemblyVersion(程序集版本号)和PublicKeyToken(公钥标记),哪怕磁盘上的dll文件版本是12.0.3.23909,只要它的实际程序集版本、公钥标记和清单记录不匹配,就会被判定为不符合加载要求,抛出“找不到程序集”的错误。
单独跑配套WinForm程序正常,是因为WinForm的基础探测路径就是自己的bin输出目录,Newtonsoft.Json和exe在同一路径下,完全满足探测和绑定要求,自然不会出错。
1. 先核对程序集的实际绑定标识
不要只看文件资源管理器属性里的版本号(那个是FileVersion文件版本,和.NET加载用的AssemblyVersion不是一回事),用PowerShell执行下面的命令,拿到真实的绑定标识:
# 查询你自己的插件dll记录的Newtonsoft.Json绑定要求 [System.Reflection.Assembly]::ReflectionOnlyLoadFrom("你的插件dll绝对路径").GetReferencedAssemblies() | Where-Object Name -eq "Newtonsoft.Json" # 查询插件目录下存放的Newtonsoft.Json.dll的实际程序集版本、公钥标记 [System.Reflection.AssemblyName]::GetAssemblyName("插件目录下Newtonsoft.Json.dll的绝对路径")
重点核对两个值:
- 插件清单记录的Newtonsoft.Json的AssemblyVersion、PublicKeyToken,和磁盘上dll的实际值是否完全一致
- 第三方主程序根目录下如果已经存在Newtonsoft.Json.dll,同样查一下它的标识,看和你引用的是否匹配
小提示:官方Newtonsoft.Json从12.0.1版本开始,会把AssemblyVersion固定锁定为12.0.0.0,不管补丁版本升级到12.0.3、甚至13.0.3,程序集版本号都不会变,只是FileVersion会跟着更新,这也是你看到引用标识是12.0.0.0、实际文件版本是12.0.3.23909的原因,本身版本号是匹配的,问题大多出在探测路径上。
2. 验证探测路径问题
先把同版本的Newtonsoft.Json.dll直接复制到第三方主程序的exe所在根目录,重启主程序加载插件测试:
- 如果报错消失,就确定是宿主的探测路径不覆盖插件子目录导致的加载失败
- 这时候不要把dll长期放在宿主根目录,容易造成全局依赖版本冲突,最好的方案是在插件的入口初始化逻辑第一时间,挂载程序集加载拦截事件,手动处理Newtonsoft.Json的加载请求:
注意:这个拦截事件必须在第一次调用Newtonsoft.Json的代码之前挂载,晚于加载时机的话,拦截逻辑不会生效。// 在插件入口类的静态构造、或者入口方法的最开始执行 AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { var requestedName = new AssemblyName(args.Name); // 匹配需要加载的Newtonsoft.Json组件 if (requestedName.Name.Equals("Newtonsoft.Json", StringComparison.OrdinalIgnoreCase)) { // 拼接插件目录下的Newtonsoft.Json.dll绝对路径 var dllPath = Path.Combine( AppDomain.CurrentDomain.BaseDirectory, "插件所在的相对子目录", "Newtonsoft.Json.dll"); if (File.Exists(dllPath)) { // 加载指定路径的dll return Assembly.LoadFrom(dllPath); } } // 其他程序集走默认加载逻辑 return null; };
3. 处理版本绑定冲突
如果查到宿主根目录已经存在其他版本的Newtonsoft.Json(比如宿主用的是11.x版本,程序集版本是11.0.0.0,公钥标记和你一致),直接在宿主根目录放12.x的dll会打乱宿主的原有依赖逻辑,这时候可以两个方案二选一:
- 如果宿主的Newtonsoft.Json版本和你用的12.x完全兼容,直接删除插件目录下的Newtonsoft.Json.dll,复用宿主已经加载到内存的版本,减少重复加载开销
- 如果版本不兼容,就用上面提到的
AssemblyResolve拦截逻辑,把你插件目录下的12.x版本隔离加载,不影响宿主的原有组件加载。
4. 排查强名称不匹配问题
如果核对的时候发现磁盘上的Newtonsoft.Json.dll的PublicKeyToken和你插件清单记录的不一样,大概率是之前引用过非官方编译的无强名称签名的版本,直接从Nuget重新拉取官方的12.x版本替换,就可以解决公钥不匹配导致的加载失败问题。
内容的提问来源于stack exchange,提问作者rst

