如何解决NuGet依赖地狱 [英] How to resolve NuGet dependency hell

查看:213
本文介绍了如何解决NuGet依赖地狱的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我开发了一个名为 CompanyName.SDK 的库,该库必须集成到公司项目 CompanyName.SomeSolution

I develop a library with some functional named CompanyName.SDK which must be integrated in company project CompanyName.SomeSolution

CompanyName.SDK.dll 必须通过NuGet软件包进行部署。
CompanyName.SDK 程序包依赖于第三方的NuGet程序包。举一个很好的例子,我们以 Unity 为例。当前依赖于 Unity v3.5.1405-prerelease

CompanyName.SDK.dll must be deployed via NuGet package. And CompanyName.SDK package has a dependency on 3rd party NuGet packages. For good example, let's take Unity. Current dependency is on v3.5.1405-prerelease of Unity.

CompanyName.SomeSolution.Project1 取决于 Unity v2.1.505.2
CompanyName.SomeSolution.Project2 取决于 Unity v3.0.1304.1

CompanyName.SomeSolution.Project1 depends on Unity v2.1.505.2. CompanyName.SomeSolution.Project2 depends on Unity v3.0.1304.1.

CompanyName.SDK 集成到此解决方案中会增加对 Unity v3.5.1405-prerelease
让我们假设 CompanyName.SomeSolution 有一个可运行的输出项目 CompanyName.SomeSolution.Application 取决于两个上面和 CompanyName.SDK

Integrating CompanyName.SDK into this solution adds dependency on Unity v3.5.1405-prerelease. Let's take that CompanyName.SomeSolution has one runnable output project CompanyName.SomeSolution.Application that depends on two above and on CompanyName.SDK

,这里开始出现问题。所有 Unity 程序集在所有没有版本说明符的软件包中都具有相同的名称。并且在目标文件夹中,它只是 Unity 程序集的一个版本: v3.5.1405-prerelease 通过<$ c app.config 中的$ c> bindingRedirect 。

And here problems begin. All Unity assemblies has equal names in all packages without version specifier. And in the target folder it will be only one version of Unity assemblies: v3.5.1405-prerelease via bindingRedirect in app.config.

如何编码 Project1 Project2 SDK 中的版本

How can code in Project1, Project2 and SDK use exactly needed versions of dependent packages they were coded, compiled and tested with?

NOTE1: Unity 只是一个例子,实际情况比依赖于另一个3rdparty模块(同时具有3-4个版本)的3rdparty模块差10倍。

NOTE1: Unity is just an example, real situation is 10 times worse with 3rdparty modules dependent on another 3rdparty modules which in turn has 3-4 versions simultaneously.

注意2:我无法将所有软件包升级到最新版本,因为有些软件包具有其他软件包的最新版本依赖性。

NOTE2: I cannot upgrade all packages to their latest versions because there are packages that have dependency not-on-latest-version of another packages.

NOTE3:假设从属软件包在各个版本之间有重大更改。这是真正的问题,为什么我要问这个问题。

NOTE3: Suppose dependent packages has breaking changes between versions. It is the real problem why I'm asking this question.

注4:我知道关于问题关于同一从属程序集的不同版本之间的冲突,但答案并没有解决问题的根源-他们只是将其隐藏。

NOTE4: I know about question about conflicts between different versions of the same dependent assembly but answers there does not solve the root of a problem - they just hide it.

注5:到底哪里出现了 DLL Hell问题解决方案?它只是从另一个位置重新出现。

NOTE5: Where the hell is that promised "DLL Hell" problem solution? It is just reappearing from another position.

注6:如果您认为使用GAC可以作为一种选择,请编写分步指南

NOTE6: If you think that using GAC is somehow an option then write step-by-step guide please or give me some link.

推荐答案

Unity 软件包不是一个很好的例子,因为您只能在名为 Composition Root 的地方使用它。并且组成根应该与应用程序入口点尽可能接近。在您的示例中,它是 CompanyName.SomeSolution.Application

Unity package isn't a good example because you should use it only in one place called Composition Root. And Composition Root should be as close as it can be to application entry point. In your example it is CompanyName.SomeSolution.Application

除此之外,我现在工作的地方完全相同问题出现。而且我看到的是,问题通常是由诸如日志记录之类的跨领域问题引入的。您可以应用的解决方案是将第三方依赖关系转换为第一方依赖关系。您可以通过为这些概念引入抽象来做到这一点。实际上,这样做还有其他好处,例如:

Apart from that, where I work now, exactly the same problem appears. And what I see, the problem is often introduced by cross-cutting concerns like logging. The solution you can apply is to convert your third-party dependencies to first-party dependencies. You can do that by introducing abstractions for that concepts. Actually, doing this have other benefits like:


  • 更多可维护代码

  • 更好的 testability

  • 摆脱不必要的依赖关系(每个 CompanyName.SDK 的客户确实需要 Unity 依赖项?)

  • more maintainable code
  • better testability
  • get rid of unwanted dependency (every client of CompanyName.SDK really needs the Unity dependency?)

因此,我们以一个虚构的<$为例c $ c> .NET Logging 库:

So, let's take for an example imaginary .NET Logging library:

CompanyName.SDK.dll 取决于在 .NET日志记录上

CompanyName.SomeSolution.Project1 取决于 .NET记录2.0

CompanyName.SomeSolution.Project2 取决于 .NET记录1.0

CompanyName.SDK.dll depends on .NET Logging 3.0
CompanyName.SomeSolution.Project1 depends on .NET Logging 2.0
CompanyName.SomeSolution.Project2 depends on .NET Logging 1.0

.NET Logging 的版本之间存在重大更改。

There are breaking changes between versions of .NET Logging.

您可以通过引入 ILogger 接口来创建自己的第一方依赖性:

You can create your own first-party dependency by introducing ILogger interface:

public interface ILogger
{
    void LogWarning();
    void LogError();
    void LogInfo();
} 

CompanyName.SomeSolution.Project1 CompanyName.SomeSolution.Project2 应该使用 ILogger 接口。它们依赖于 ILogger 接口第一方依赖性。现在,您将 .NET Logging 库保留在一个地方,并且更新很容易,因为您必须将其放在一个地方。同样,打破版本之间的更改也不再是问题,因为使用了 .NET Logging 库的一个版本。

CompanyName.SomeSolution.Project1 and CompanyName.SomeSolution.Project2 should use ILogger interface. They are dependent on ILogger interface first-party dependency. Now you keep that .NET Logging library behind one place and it's easy to perform update because you have to do it in one place. Also breaking changes between versions are no longer a problem, because one version of .NET Logging library is used.

ILogger 接口的实际实现应使用不同的程序集,并且应仅在引用 .NET Logging 库的地方。
CompanyName.SomeSolution.Application 中,在您编写应用程序的位置,现在应将 ILogger 抽象映射到具体的实现。

The actual implementation of ILogger interface should be in different assembly and it should be only place where you reference .NET Logging library. In CompanyName.SomeSolution.Application in place where you compose your application you should now map ILogger abstraction to concrete implementation.

我们正在使用这种方法,我们还使用了 NuGet 来分发我们的抽象和实现。不幸的是,版本问题会随您自己的软件包一起出现。为避免该问题,请在通过 NuGet 为公司部署的软件包中使用语义版本控制。如果代码库中通过 NuGet 分发的某些更改,则应更改通过 NuGet 。例如,我们在本地 NuGet 服务器中:

We are using that approach and we are also using NuGet for distribute our abstractions and our implementations. Unfortunately, issues with versions can appear with your own packages. To avoid that issues apply Semantic Versioning in packages you deploy via NuGet for your company. If something change in in your code base that is distributed via NuGet you should change in all of the packages that are distributed via NuGet. For example we have in our local NuGet server :


  • DomainModel

  • Services.Implementation.SomeFancyMessagingLibrary (引用 DomainModel SomeFancyMessagingLibrary

  • 以及更多...

  • DomainModel
  • Services.Implementation.SomeFancyMessagingLibrary (that references DomainModel and SomeFancyMessagingLibrary)
  • and more...

此软件包之间的版本是同步的,如果在 DomainModel 中更改了版本,则在 Services中将使用相同的版本。 Implementation.SomeFancyMessagingLibrary 。如果我们的应用程序需要更新内部软件包,则所有依赖项都将更新为相同版本。

Version between this packages are synchronized, if version is changed in DomainModel, the same version is in Services.Implementation.SomeFancyMessagingLibrary. If our applications needs update of our internal packages all dependencies are updated to the same version.

这篇关于如何解决NuGet依赖地狱的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆