Newtonsoft Json.NET 版本不兼容(DLL 地狱)

Posted

技术标签:

【中文标题】Newtonsoft Json.NET 版本不兼容(DLL 地狱)【英文标题】:Newtonsoft Json.NET version incompatibility (DLL hell) 【发布时间】:2015-10-15 00:50:21 【问题描述】:

Newtonsoft.Json 发布不兼容的版本,强名称相同,仅更改文件版本。

根据MSDN:

具有相同强名称的程序集应该相同。

因此,如果我们无法控制的其他应用程序将不同版本的 Newtonsoft.Json.dll 放入 GAC,我们的应用程序就会中断。

有没有办法强制 .NET 加载我们需要的特定版本?

更新:

让我更深入地解释一下这个问题:

据我所知,在 .NET 中,没有机制在 CLR 尝试解析程序集并失败之前解析程序集。

只有 AppDomain.AssemblyResolve 事件,它仅在程序集未解析时触发。通常就足够了。

但如果是 Newtonsoft.Json,它并没有无法解析程序集,但它只是加载了错误的程序集。

这是因为 Newtonsoft.Json 发布了具有相同强名称的不兼容版本。

示例:

假设我们的应用程序针对 N.J.dll 编译(程序集版本 1.0,文件版本 1.0)

然后是其他一些应用,把其他不兼容版本的同一个dll放到GAC N.J.dll中(汇编版本1.0,文件版本1.1

因为它们只改变文件版本而不改变程序集版本,所以这两个程序集具有相同的强名称。

所以对于我们的应用程序 .NET 尝试解析 N.J.dll(程序集版本 1.0)它会在 GAC 中看到 dll 并加载它。 (因为 .NET 总是更喜欢 GAC 中的程序集而不是“bin”文件夹中的程序集

但是加载的程序集是错误的。它的文件版本为 1.1,与 1.0 版本不兼容。

由于两个程序集具有相同的程序集版本,.NET 看不到它们之间的任何差异。但是当它实际上试图解析内部的某个类或成员时,它失败了,因为它在 1.1 版中被更改了。

整个应用程序因不可预知的错误而失败。

最糟糕的是,即使我的应用程序没有将 newtonsoft.json.dll 放入 GAC,我无法控制的其他一些应用程序会将不同版本的 newtonsoft.json.dll 放入 GAC - 我的应用程序将打破不可预知的异常。

所以我的问题是,我可以先加载正确的程序集,然后再加载错误的程序集吗?

更新

https://github.com/JamesNK/Newtonsoft.Json/issues/615 https://github.com/JamesNK/Newtonsoft.Json/issues/1001

这个问题的问题已经结束,评论表明 Newtonsoft.Json 的作者不了解 .NET 版本控制以及为什么这很重要。

【问题讨论】:

这种情况甚至发生在 Microsoft 之前 - J# 重新发布,但程序集版本未更改。如果您在安装过程中检测到它,您可能会要求用户在 GAC 中安装正确版本的程序集。另一种选择是从相同的来源生成您自己的二进制文件并根据需要为其分配强名称(如果许可证允许) 问题更大。 Microsoft 本身在其库和 SDK 中使用 Newtonsoft Json.NET。所以任何使用它们的东西都可能随时出现非常奇怪的错误。 这终于解释了我半年前在一个 MVC Web API 应用程序中收到的一堆意外异常。该应用程序使用的 Json.Net 版本高于与 WebAPI 本身捆绑的版本。该应用程序在部署后立即运行良好,直到 IIS 决定暂停它(大约 30 分钟没有使用后)。当有人在暂停后访问应用程序时,IIS 尝试再次启动它,但不知何故弄乱了 Json.Net 版本,我的工作应用程序臭名昭著地死了。我在 GAC 中没有 Json.Net,只使用 NuGet 来管理我的依赖项。 您是正确的,newtonsoft 不会与 AssemblyFileVersion 一起更新 AssemblyVersion。通常他们的文件或产品版本与他们的包版本相匹配。文件和产品版本都是 win32 概念。因此,例如,对于 6.0.4 和 6.0.8 包,它们使用相同的程序集版本,并且我相信,它们还可以确保它们完全向后/向前兼容。只有更改必须只是错误修复。因此,理想情况下,这不应破坏您的应用程序。但我同意他们不应该遵循这种做法。 【参考方案1】:

程序集加载器只会探测丢失的程序集,即尚未加载的程序集。如果将 DLL 部署到应用程序安装文件夹,然后在应用程序启动时显式加载它,则程序集加载器将不会尝试从 GAC 再次加载它。

您可以使用Assembly.LoadFrom 方法显式加载程序集。

更多信息请参见https://msdn.microsoft.com/en-us/library/dd153782(v=vs.110).aspx。

【讨论】:

是的,但很难实现。因为它需要在初始化任何使用给定程序集的类型之前发生。它需要很早,我不知道任何事件或入口点可靠地发生在那个早 它是什么类型的应用程序?这是一个网站,控制台应用程序...几乎总是有一个启动位置,您可以在其中放置此代码,但具体取决于应用程序的类型。 您也可以创建一个加载器应用程序。加载器应用程序将创建一个新的应用程序域,在调用应用程序的入口点运行它之前,将应用程序的 dll 显式加载到新的应用程序域中。这是我能想象到的最极端的解决方案,对于你的情况几乎肯定没有必要。 另一个选项是动态绑定到 Newtonsoft 程序集,而不是创建静态绑定。这将涉及按名称加载类型并查找构造函数和要使用反射调用的方法。仅当您仅在应用程序的几个地方使用 Newtonsoft 时,此方法才实用。 LoadFrom() 和 LoadFile() 不一定会从文件中加载请求的程序集。如果具有其标识的程序集在 GAC 中,则将使用 GAC 副本。

以上是关于Newtonsoft Json.NET 版本不兼容(DLL 地狱)的主要内容,如果未能解决你的问题,请参考以下文章

无法加载文件或程序集“Newtonsoft.Json.Net”(HRESULT 异常:0x80131040)

未能加载文件或程序集“Newtonsoft.Json, Version=4.5.0.0, Culture=neutral,解决

Newtonsoft.Json(Json.net) 的使用

Newtonsoft.Json Json.Net02.02序列化指引Serialization Guide

Newtonsoft.Json(Json.Net)学习笔记

Json.NET 与 Newtonsoft.Json SerializationBinder 不同