为啥强制降级会导致 .Net Core 中的程序集加载异常?

Posted

技术标签:

【中文标题】为啥强制降级会导致 .Net Core 中的程序集加载异常?【英文标题】:Why does a force downgrade causes an assembly load exception in .Net Core?为什么强制降级会导致 .Net Core 中的程序集加载异常? 【发布时间】:2021-12-19 11:14:39 【问题描述】:

我有一个包含控制台和库项目的示例解决方案。两者都引用了相同的 nuget,但版本不同。控制台项目也有对库项目的引用。所以结构是这样的:

- Solution
    - ConsoleApp
        - Project Reference: Library
        - Nuget: NServiceBus.RabbitMQ (5.2.0)
    - Library
        - Nuget: NServiceBus.RabbitMQ (6.0.0)
     

您可以找到解决方案here。

由于 Nuget 使用最近获胜规则,因此解析的 nuget 包是版本 5.2.0。这就是我想要的,到目前为止一切顺利。但是当我运行应用程序并运行库的方法时,我得到以下异常:

Could not load file or assembly 'NServiceBus.Transport.RabbitMQ, Version=6.0.0.0, Culture=neutral, PublicKeyToken=9fc386479f8a226c'. The located assembly's manifest definition does not match the assembly reference. (0x80131040)

在 .NET Framework 中,我将通过程序集重定向来解决这个问题。但这在 .Net Core 中不可用。我一直认为 .Net Core 通过使用 deps.json 文件自动解决了这个问题。在那里我看到以下声明:

"Library/1.0.0": 
    "dependencies": 
        "NServiceBus.RabbitMQ": "5.2.0"
    ,
    "runtime": 
        "Library.dll": 
    

但他仍然在运行时尝试解析 6.0.0 版本。我正在使用最新的 .Dot Net 3.1.X SDK。

我做错了什么还是这看起来像一个错误?

为了记录,这是一个简单的示例项目。我需要这个的实际情况要复杂得多。我也明白这样做可能会在运行应用程序时导致运行时异常。

【问题讨论】:

您确定,不同的主编号不是主要问题吗?我会尽量避免混合同一个 nuget 的主要版本。新的主要号码通常会说“我们在这里有重大变化”。 就最佳实践而言,您是绝对正确的。但有时您知道您没有调用任何具有重大更改的代码。如果你依赖一些没有匹配依赖的外部库,这个技巧在某些情况下会很方便。 【参考方案1】:

这似乎是设计使然。

稍微搜索了一下,找到了这个:https://github.com/dotnet/fsharp/issues/3408#issuecomment-319466999

coreclr 将加载版本或高于参考的程序集。如果发现的程序集低于参考,则失败。

还有这个:https://github.com/dotnet/sdk/issues/384#issuecomment-260457776

.NET Core 不支持降级程序集版本

因此,为了确认,我花在查看/搜索 https://github.com/dotnet/runtime 上的时间比我预期的要多得多。最终找到了汇编版本兼容方法:https://github.com/dotnet/runtime/blob/172059af6623d04fa0468ec286ab2c240409abe3/src/coreclr/binder/assemblybindercommon.cpp#L49-L53

它分别检查版本的所有组件,但如果我们只看一个,我们可以看到它在做什么:

            if (!pFoundVersion->HasMajor() || pRequestedVersion->GetMajor() > pFoundVersion->GetMajor())
            
                // - A specific requested version component does not match an unspecified value for the same component in
                //   the found version, regardless of lesser-order version components
                // - Or, the requested version is greater than the found version
                return false;
            

正如评论所说,如果程序集的版本低于请求的版本,加载程序将拒绝该程序集。在您的情况下,假设程序集版本与包版本匹配(它不必),您的库正在请求版本 6.0.0,但程序集加载器/绑定器在磁盘上找到版本 5.2.0,该版本较低.因此,它拒绝该 dll,继续查找,但在探测路径上找不到合适的程序集版本,并最终引发 FileLoadException。

我不清楚的是,是否仅在默认程序集加载器上检查此程序集兼容性,或者即使您将自己的事件处理程序添加到 AssemblyLoadContext.Default.Resolving。您可以尝试添加自己的处理程序,当它请求更高版本的程序集时,您无论如何都会返回较低版本的程序集。这可能是解决问题的一种方法。

【讨论】:

非常感谢您的回答!我在谷歌上找不到任何关于它的信息。我想我应该开始在 GitHub 上搜索更多内容。对我们来说,很遗憾 .Net Core 不再支持它。这是我们在开发框架库时必须考虑的事情。如果我们增加一个依赖版本,这意味着使用该框架的每个人都必须遵循。如果严格遵循 semver 版本控制,这是一件好事,但是很多库不包含对主要版本的重大更改。所以在这些情况下,它的灵活性较差。

以上是关于为啥强制降级会导致 .Net Core 中的程序集加载异常?的主要内容,如果未能解决你的问题,请参考以下文章

如何用 ASP.NET Core 实现熔断和降级?

为啥 .Net 4.0 构建客户端 DataContracts 会导致 .Net 4.5 应用程序中的 MethodAccessException?

为啥将返回类型添加到 void 返回方法会导致 MissingMethodException

为啥 Gradle 会降级 Grails 3.1 应用程序中的传递依赖项?

对控制器函数的多次调用会导致问题(ASP.NET MVC Web 应用程序中的 EF Core)

为啥运行时表达式会导致 Entity Framework Core 5 的缓存发生冲突?