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

Why does a force downgrade causes an assembly load exception in .Net Core?

提问人:Dommicentl 提问时间:11/5/2021 更新时间:11/5/2021 访问量:274

问:

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

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

您可以在此处找到解决方案。

由于 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。

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

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

.net-core 程序集加载. nuget

评论

0赞 Lukasz Szczygielek 11/5/2021
你确定,不同的主要数字不是主要问题吗?我会尽量避免混合同一 nuget 的主要版本。新的主要数字通常说“我们这里有重大变化”。
0赞 Dommicentl 11/8/2021
就最佳实践而言,您是绝对正确的。但有时您知道您没有调用任何具有重大更改的代码。如果你依赖于一些没有匹配依赖关系的外部库,这个技巧在某些情况下会很方便。

答:

1赞 zivkan 11/5/2021 #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默认正在解决。您可以尝试添加自己的处理程序,当它请求较高版本的程序集时,您仍然返回较低版本的程序集。这可能是解决该问题的一种方式。

评论

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