【问题标题】:Multiple references to the same class library对同一个类库的多次引用
【发布时间】:2013-04-01 23:16:12
【问题描述】:

假设 2 年前,我编写了一个 .NET 类库,比如 mylib.dll,它在很大程度上依赖于一个外部库,比如 foo.dll,它是开源的、MIT 许可的(这是相关的,请继续阅读) .

很多客户使用我的库以及它的依赖程序集。包是mylib.dllfoo.dll

几天过去了,foo.dll 的创建者破坏了他们的 API,现在他们部署了一个新版本的库。这个新版本包含了旧版本中包含的大部分类型,但其中一些方法丢失或更改了。

问题

一些潜在的消费者已经在他们的应用程序中使用foo.dll 的新版本,供他们自己使用。当我向他们提供mylib.dll 时,他们无法使用它。他们甚至无法成功构建,因为mylib.dll 想要旧版本的foo.dll 及其旧方法和签名。

如何解决这个问题?

我想要一个前向兼容的解决方案,这样这个问题就不会再次出现。于是我想到了几件事:

  1. 更新我的源代码以使用新版本的foo.dll。但这需要大量的工作和时间。此外,这不是向前兼容的,因为这个问题可能会在某一天再次出现。
  2. 既然我有foo.dll 的确切版本的来源,也许我可以轻松地更改它的命名空间并重建整个东西。这样我就可以享受我自己的foo.dll 版本,这是合法的,因为它的许可证是 MIT。我可以将修改后的程序集命名为 mylib.foo.dll 或类似名称。我什至可以使用ILMerge 将整个内容合并到mylib.merged.dll 之类的内容中。
  3. 重新设计mylib.dll,以便将foo.dll 所需的功能分离为接口和实现。在工程方面,这是最优雅的解决方案。就时间而言,这是一场灾难,因此不能被视为真正的解决方案。

我的问题

要完成上面的#2,有没有一种捷径可以改变给定源代码的命名空间?

【问题讨论】:

  • 为什么不使用简单的查找-替换操作呢?您只需要将foo 的所有文件中的namespace foo 替换为namespace mylib.foo。然后您必须将using foo 替换为using mylib.foo。完成。
  • @RB,我认为您在这里忽略了重点,或者我不明白您的建议。您是否建议新消费者使用旧版本的foo.dll?如果是这样,那么从我公司的角度来看,这不是一个明智的商业决策。
  • @RonKlein 但是您在选项 2 中的建议肯定是您将分叉 foo.dll,因此您的客户无论如何都会拥有它的旧版本(我想您不太可能维护它与原始作者的程度相同-我知道我不会!)。还是我完全误解了?
  • (续)如果不创建自己的foo 分支,客户应用程序的文件夹中需要有两个不同版本的foo.dll,这显然是不可能的。
  • @RB.:您错过了客户应用程序 mylib 不使用 GAC,因此他们都希望 foo.dll 位于应用程序文件夹中.但他们需要不同的版本。这与.NET 无关。这只是因为文件系统只关心文件的名称,而不关心文件的版本。

标签: .net dependencies versioning forward-compatibility


【解决方案1】:

啊哈!是的 - 这是 流行 开源库(例如 nlog、newtonsoft 等)的消费者的常见痛点。

1.允许多个 FOO.DLL(s)

您的问题源于您的输出目录只能包含一个 DLL 副本(因为所有版本共享相同的文件名)。有些产品将版本号放入 DLL 文件名来解决这个问题——但我认为这很难看。您可以使用程序集绑定重定向将您的 DLL 依赖项存储在 bin/子文件夹中,以防止覆盖 - 但这是一个 hack,可以通过以下解决方案更好地解决......

2。合并

您的客户最简单的解决方案是将您的程序集及其依赖项打包到一个多模块 DLL 中 - 即将ILMerge to MERGE foo.dll 使用到您的 mylib.dll 中(是的,一个 DLL 可以包含多个程序集) - 那么您的产品似乎是无依赖关系的(实际上并非如此,它的依赖关系只是在一个 DLL 中提供!太好了!)。

这是一个比尝试重新编译 foo.dll 或命名空间搜索/替换更好的解决方案,因为您不必弄乱依赖源代码并冒险破坏它...开始更改第 3 方源代码,您必须维护这些更改 - 当他们发布新版本时...在这里您再次搜索/替换...

问题解决了。

警告:请记住,如果您的 mylib.dll 将 foo.dll 中声明的类型返回给调用程序(您客户的程序),他们可能会收到类转换异常,因为他们的 DLL 需要不同版本的类型。因此,仅当您对 foo.dll 的依赖是黑盒(未公开)时,才建议使用此解决方案。但是,我希望这对您来说不是问题,或者您不会考虑将 FOO 类型移至不同的命名空间。

【讨论】:

  • 选项 (1) 需要更多的编程,因此可能很耗时且有点风险。至于选项(2),我什至在提出问题之前就考虑过,并尝试过(与您建议的相同)。 ILMerge 似乎不是这样工作的。它只是将程序集组合到一个文件中,但程序集解析仍然是一个问题。你自己试过吗?
  • 嗨,罗恩,我不明白你的意思是“组装解析不能像这样工作”。我能想到这不能正常运行的唯一原因是 Foo.DLL 没有强命名。如果是这种情况,您无能为力 - 加载到运行时的程序集的第一个副本将是由所有 dll 引用解析的那个 - 在这种情况下,我假设您收到“MemberNotFound Exceptions”?解决方案是强烈命名您的 Foo.dll 副本,然后对其进行 ILmerge。
  • 我刚刚找到了这个解释,应该可以帮助您完成这些步骤:coderwall.com/p/vjynhq
【解决方案2】:

如果您不使用旧foo.dll 中缺少的许多新方法,一个可行的解决方案可能是将您的代码链接到新foo.dll,并制作类似mylib.foo.legacy.dll 的东西包含缺少的函数,您可以从旧的 foo 源中复制这些函数,因为它是开源的并获得 MIT 许可。

无论你做什么,都以foo.dll 的最新版本为基础,作者不太可能回到旧功能。如果您使用 [obsolete] 属性标记从旧 dll 复制的旧方法,您将收到编译警告,显示仍然引用旧函数的位置。然后,随着时间的推移,您可以努力删除这些引用,直到可以完全删除旧版 dll。

【讨论】:

    猜你喜欢
    • 2018-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多