如何解决NuGet依赖地狱 [英] How to resolve NuGet dependency hell
问题描述
我开发了一个名为 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 theUnity
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 $ c $分发的所有软件包。 c>。例如,我们在本地
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 referencesDomainModel
andSomeFancyMessagingLibrary
)- 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屋!