.NET与.NET Framework动态加载程序集的传递解析差异
.NET 7与.NET Framework 4.6.1程序集加载行为差异原因分析
问题场景
面向.NET 7和.NET Framework 4.6.1的应用在CLR程序集加载行为上存在明显差异:
基于一套插件系统,核心是「参考控制台应用」,引用所有netstandard2.0类库,构建后发布到对应平台专属文件夹,其他应用通过该文件夹动态加载程序集。
复现项目架构:
- TestContracts:netstandard2.0项目,暴露
ITracer接口; - TestLibrary:netstandard2.0项目,依赖TestContracts实现
Tracer类,同时依赖Elastic.Clients.Elasticsearch NuGet包,该包传递依赖System.Text.Json v8.0.5(对应程序集版本8.0.0.0); - TestReferenceApp:多目标net7.0和net461,引用TestContracts和TestLibrary,通过
dotnet publish发布到PublishedAppNet70和PublishedAppNet461文件夹; - TestApp:入口项目仅依赖TestContracts,通过
Assembly.LoadFrom从平台发布文件夹加载TestLibrary,实例化Tracer并转为ITracer接口。调用ITracer.Trace方法时,.NET 7版本抛出System.IO.FileLoadException(提示无法加载System.Text.Json 8.0.0.0),而.NET Framework 4.6.1版本运行正常。
差异核心原因
1. 程序集加载上下文的依赖搜索逻辑不同
- .NET Framework 4.6.1的传统CLR中,
Assembly.LoadFrom加载的程序集会被放入LoadFrom上下文,CLR会自动从该程序集的所在目录搜索其依赖项(包括传递依赖)。由于PublishedAppNet461目录已包含System.Text.Json.dll(发布时从NuGet包复制而来),因此加载时能直接找到。 - .NET 7的CoreCLR中,
Assembly.LoadFrom加载的程序集同样进入LoadFrom上下文,但CoreCLR不会自动将该程序集的目录加入依赖搜索路径,默认优先从TestApp的运行基目录查找依赖。而TestApp本身未引用System.Text.Json,因此无法找到对应的程序集。
2. 发布阶段的依赖复制规则差异
- 针对.NET Framework 4.6.1的
dotnet publish会默认将所有依赖程序集(包括NuGet传递依赖)复制到发布目录,确保PublishedAppNet461中存在System.Text.Json.dll。 - 针对.NET 7的
dotnet publish(默认框架依赖部署模式),虽然也会复制第三方依赖到发布目录,但由于CoreCLR的加载逻辑限制,TestApp运行时不会主动去PublishedAppNet70目录搜索该依赖,导致加载失败。
3. 程序集解析与绑定机制的区别
- .NET Framework依赖绑定重定向机制,默认自动处理版本冲突,同时LoadFrom上下文的依赖查找逻辑会优先使用自身所在目录,进一步保证了依赖能被找到。
- .NET 7移除了自动绑定重定向(需手动配置),且CoreCLR没有将LoadFrom上下文程序集的目录纳入全局搜索路径。若未通过
AssemblyLoadContext.Resolving事件手动指定依赖加载路径,就会出现找不到依赖的异常。
内容的提问来源于stack exchange,提问作者Palo Mraz
相关产品推荐
相关产品推荐

