如何定位System.IO.FileNotFoundException加载Telerik程序集的根因
问题现象
当前运行环境存在两个不可修改的主程序:
- 服务端:
<Application_Server>.exe - 客户端:
<Application>.Desktop.exe
两个程序均支持通过运行目录下Modules子目录放置DLL实现功能扩展。
上周客户端可正常启动,当前启动抛出System.IO.FileNotFoundException异常,具体信息:
无法加载程序集'Telerik.OpenAccess.35.Extensions, Version=2016.2.822.1, Culture=neutral, PublicKeyToken=7ce17eeaf1d59342'或其依赖项
已知上周运行目录同样不存在该Telerik程序集,程序可正常运行。从NLog日志、程序集绑定详情、异常调用栈可确认:
- 异常触发点为Prism框架
DirectoryModuleCatalog反射扫描目录内程序集导出类型的环节 - 属于反射仅加载上下文的绑定失败
- CLR已依次在应用根目录、
Modules目录下查找对应DLL/EXE文件,均未命中
根因排查方向
1. 优先排查Modules目录的DLL变动
从出问题的时间倒推,只要这期间往Modules目录放了间接引用Telerik.OpenAccess.35.Extensions的DLL,就会触发这个报错:
- Prism的
DirectoryModuleCatalog是固定逻辑,会扫描目录下所有DLL的所有导出类型,不会提前过滤非模块类、不会跳过缺依赖的程序集 - 哪怕新增的DLL本身代码里根本没用到Telerik相关功能,只要它引用的某个第三方库带了这个Telerik依赖,反射扫类型的时候CLR就会顺着依赖链查找所有关联程序集,找不到就直接抛错
- 上周能正常运行,核心原因是当时Modules目录下没有任何带这个Telerik依赖的程序集,根本不会触发对应加载流程
- 排查操作:把Modules下的DLL按修改时间倒序排列,从最新的开始逐个移出目录测试启动,定位到触发问题的具体DLL后,查看它的程序集引用列表即可确认。
2. 排查全局程序集缓存(GAC)的变动
如果上周安装过带Telerik组件的工具、卸载过软件、安装过系统更新,很可能把之前存在GAC里的对应版本Telerik程序集删掉了:
- 之前本地目录没放这个DLL也能跑,大概率是GAC里存在对应版本,CLR反射加载时优先从GAC命中了程序集
- 一旦GAC里的对应版本被移除,本地又没有对应文件,自然会抛出找不到文件的异常
- 排查操作:打开VS开发者命令提示符,执行
gacutil /l Telerik.OpenAccess.35.Extensions,查看返回结果里有没有Version=2016.2.822.1, PublicKeyToken=7ce17eeaf1d59342的条目,没有即可确认是该原因。
3. 排查主程序配置文件的改动
检查客户端根目录下的<Application>.Desktop.exe.config最近是否被修改:
- 查看是否存在新增的错误
<dependentAssembly>绑定重定向规则,把其他版本的Telerik程序集强制重定向到了2016.2.822.1这个不存在的版本 - 查看是否新增/修改过
<probing>节点,改动了CLR默认查找程序集的路径,导致之前能找到的程序集现在扫描不到。
无需修改主程序的修复方案
定位原因后不需要改动两个主EXE即可解决问题:
- 如果是Modules目录下新增DLL带来的非必要依赖:直接找对应版本的
Telerik.OpenAccess.35.Extensions.dll放到客户端根目录即可;如果实际业务没用到Telerik相关功能,放一个元数据匹配的同版本DLL过了加载校验即可,要是用到相关功能就放官方对应版本的DLL,避免运行时出现逻辑错误 - 如果是GAC里的程序集被删除:要么把对应版本DLL重新注册到GAC,要么直接把DLL放到客户端根目录
- 如果是配置文件绑定重定向错误:修正配置里的版本映射规则,指向本地实际存在的Telerik程序集版本即可。
内容的提问来源于stack exchange,提问作者Dominique
相关产品推荐
相关产品推荐

