问题描述
我开发了一个名为 CompanyName.SDK
的库,它必须集成在公司项目 CompanyName.SomeSolution
CompanyName.SDK.dll
必须通过 NuGet 包部署.CompanyName.SDK
包依赖于 3rd 方 NuGet 包.举个很好的例子,让我们以 Unity
为例.当前依赖于 Unity
的 v3.5.1405-prerelease
.
CompanyName.SomeSolution.Project1
依赖于 Unity
v2.1.505.2
.CompanyName.SomeSolution.Project2
依赖于 Unity
v3.0.1304.1
.
将 CompanyName.SDK
集成到此解决方案中会增加对 Unity
v3.5.1405-prerelease
的依赖.我们假设 CompanyName.SomeSolution
有一个可运行的输出项目 CompanyName.SomeSolution.Application
依赖于上述两个和 CompanyName.SDK
问题从这里开始.所有 Unity
程序集在没有版本说明符的所有包中具有相同的名称.在目标文件夹中,它将只有一个版本的 Unity
程序集:v3.5.1405-prerelease
via bindingRedirect
in app.config
.
Project1
、Project2
和 SDK
中的代码如何使用它们被编码、编译和测试的依赖包的完全需要的版本与?
NOTE1: Unity
只是一个例子,实际情况比 3rdparty 模块依赖于另一个 3rdparty 模块的情况要差 10 倍,而另一个 3rdparty 模块又同时具有 3-4 个版本.
注意 2: 我无法将所有软件包升级到最新版本,因为有些软件包依赖于其他软件包的 not-on-latest-version.
注意 3: 假设依赖包在版本之间有重大变化.这是我问这个问题的真正问题.
注意 4: 我知道问题 关于同一依赖程序集的不同版本之间的冲突,但那里的答案并不能解决问题的根源——他们只是隐藏它.
NOTE5: 承诺的DLL Hell"问题解决方案到底在哪里?它只是从另一个位置重新出现.
注意6:如果您认为使用 GAC 是一种选择,请编写分步指南或给我一些链接.
您可以在编译后汇编级别工作以解决此问题...
选项 1
您可以尝试将程序集与 ILMerge
<块引用>ilmerge/target:winexe/out:SelfContainedProgram.exe Program.exe ClassLibrary1.dll ClassLibrary2.dll
结果将是一个程序集,它是您的项目及其所需依赖项的总和.这会带来一些缺点,比如牺牲单声道支持和丢失程序集标识(名称、版本、文化等),所以当所有要合并的程序集都由您构建时,这是最好的.
所以来了……
选项 2
您可以将依赖项作为资源嵌入到您的项目中,如 这篇文章.以下是相关部分:
<块引用>在运行时,CLR 将无法找到依赖的 DLL组件,这是一个问题.要解决此问题,当您的应用程序初始化,注册一个回调方法到 AppDomain 的ResolveAssembly 事件.代码应如下所示:
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>{String resourceName = "AssemblyLoadingAndReflection."+新的 AssemblyName(args.Name).Name + ".dll";使用 (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) {字节[] 组装数据 = 新字节[流.长度];流.Read(assemblyData, 0, assemblyData.Length);返回 Assembly.Load(assemblyData);}};
<块引用>
现在,第一次线程调用一个引用类型的方法一个依赖的 DLL 文件,AssemblyResolve 事件将被引发并且上面显示的回调代码将找到所需的嵌入式 DLL 资源并通过调用 Assembly 的 Load 方法的重载来加载它将 Byte[] 作为参数.
如果我在你的情况下,我认为这是我会使用的选项,牺牲一些初始启动时间.
更新
看看这里.您还可以尝试在每个项目的 app.config 中使用那些 <probing>
标记来定义一个自定义子文件夹,以便在 CLR 搜索程序集时查看.
I develop a library with some functional named CompanyName.SDK
which must be integrated in company project CompanyName.SomeSolution
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
depends on Unity
v2.1.505.2
.
CompanyName.SomeSolution.Project2
depends on Unity
v3.0.1304.1
.
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
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
.
How can code in Project1
, Project2
and SDK
use exactly needed versions of dependent packages they were coded, compiled and tested with?
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.
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: Suppose dependent packages has breaking changes between versions. It is the real problem why I'm asking this question.
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.
NOTE5: Where the hell is that promised "DLL Hell" problem solution? It is just reappearing from another position.
NOTE6: If you think that using GAC is somehow an option then write step-by-step guide please or give me some link.
You can work at post-compilation assembly level to solve this issue with...
Option 1
You could try merging the assemblies with ILMerge
ilmerge /target:winexe /out:SelfContainedProgram.exe Program.exe ClassLibrary1.dll ClassLibrary2.dll
The result will be an assembly that is the sum of your project and its required dependencies. This comes with some drawbacks, like sacrificing mono support and losing assembly identities (name, version, culture etc.), so this is best when all the assemblies to merge are built by you.
So here comes...
Option 2
You can instead embed the dependencies as resources within your projects as described in this article. Here is the relevant part:
At run-time, the CLR won’t be able to find the dependent DLL assemblies, which is a problem. To fix this, when your application initializes, register a callback method with the AppDomain’s ResolveAssembly event. The code should look something like this:
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => {
String resourceName = "AssemblyLoadingAndReflection." +
new AssemblyName(args.Name).Name + ".dll";
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) {
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
Now, the first time a thread calls a method that references a type in a dependent DLL file, the AssemblyResolve event will be raised and the callback code shown above will find the embedded DLL resource desired and load it by calling an overload of Assembly’s Load method that takes a Byte[] as an argument.
I think this is the option i would use if I were in your shoes, sacrificing some initial startup time.
Update
Have a look here. You could also try using those <probing>
tags in each project's app.config to define a custom sub-folder to look in when the CLR searches for assemblies.
这篇关于如何解决 NuGet 依赖地狱的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!