我有一个代表我的逻辑层的类库。对于那个库我已经为Google.Apis.Analytics.v3添加了一个nuget包 - 它安装了包及其所有依赖项。
我有一个使用该逻辑类库(常规引用)的控制台应用程序。一切都写得很好。
问题是在运行时期间它抛出了一个未找到Google.Apis.dll的异常。此DLL是使用nuget下载的依赖项。
检查BIN文件夹,我发现在类库bin文件夹中存在这个DLL,但是在控制台应用程序BIN文件夹中它不存在(而其他相关的DLL是)。所以这意味着并非所有在编译期间复制的引用。
我在网上搜索过,找到了所有不起作用的解决方法(例如手动编辑项目文件并删除该dll定义中的真正xml行)。
我最终做的是将相同的nuget库添加到我的控制台应用程序中 - 它可以工作,但感觉有点脏,而不是应该的样子。我认为控制台应用程序是客户谁应该从该逻辑类库获取它的服务,应该知道它的东西没有“客户”担心它。
此外,该控制台应用程序不是唯一一个将使用该服务的人,我也计划使用该功能的网络应用程序 - 所以我需要将相同的nuget添加到该Web应用程序 - 再次,感觉有点凌乱......
只是我吗?这是正确的方法吗?我正在考虑编写一个WCF项目来处理这个功能 - 但这对于功能来说似乎只是一个开销,并且可能会减慢我的工作流程,以便在我看来保持“更清洁”。
我只是过度思考了吗?
感谢
答案 0 :(得分:31)
对于示例场景,假设我们有项目X,程序集A和程序集B.程序集A引用程序集B,因此项目X包含对A和B的引用。此外,项目X包含引用程序集A的代码(例如A.SomeFunction())。现在,您创建一个引用项目X的新项目Y。
因此依赖关系链如下所示: Y => X => A =>乙强>
Visual Studio / MSBuild试图变得聪明,只将引用引入到项目Y中,它检测到项目X需要它;这样做是为了避免项目Y中的参考污染。问题是,由于项目X实际上不包含任何明确使用程序集B的代码(例如B.SomeFunction()),VS / MSBuild不会检测到B是必需的通过X,因此不会将其复制到项目Y的bin目录中;它只复制X和A程序集。
您有两个选项可以解决此问题,这两个选项都会导致程序集B被复制到项目Y的bin目录中:
就个人而言,我个人更喜欢选项2。
以下是我遇到这种情况时通常会添加的“虚拟代码”示例。
// DO NOT DELETE THIS CODE UNLESS WE NO LONGER REQUIRE ASSEMBLY A!!!
private void DummyFunctionToMakeSureReferencesGetCopiedProperly_DO_NOT_DELETE_THIS_CODE()
{
// Assembly A is used by this file, and that assembly depends on assembly B,
// but this project does not have any code that explicitly references assembly B. Therefore, when another project references
// this project, this project's assembly and the assembly A get copied to the project's bin directory, but not
// assembly B. So in order to get the required assembly B copied over, we add some dummy code here (that never
// gets called) that references assembly B; this will flag VS/MSBuild to copy the required assembly B over as well.
var dummyType = typeof(B.SomeClass);
Console.WriteLine(dummyType.FullName);
}
答案 1 :(得分:1)
如果您有以下依赖关系链: Lib1< - Lib2< - MyApp
TLDR版本:通过不进行假设,构建系统避免引入不确定性/意外行为。
构建MyApp时,Lib2将被复制到MyApp的bin目录中,但Lib1不会。您需要在MyApp中添加对Lib2 和 Lib1的引用,以便在MyApp的bin目录中获取Lib1的dll(否则您将收到运行时错误)。确定最终存在于Lib2的bin目录中的确切文件集是不可能的(或者可能真的很难),这些文件是安全的。适合复制到MyApp的。如果构建系统假设Lib2的bin目录中的所有内容对MyApp都是安全的,或者它为你隐式引用了Lib1,那么它可能会无意中改变MyApp的行为。
想象一个解决方案,其中多个项目依赖于Lib2,但其中一个项目想要使用Assembly.LoadFrom / Activator.CreateInstance / MEF / etc加载相邻的.dll文件。 (一个插件)而另一个没有。自动复制操作可以将Lib2与插件dll一起抓取并将其复制到第一个和第二个项目的构建目录(因为它是由于构建操作而在Lib2的bin目录中)。这会改变第二个应用程序的行为。
或者,如果它更聪明,并且在您引用Lib2时隐式引用了Lib1(并且不仅仅复制bin目录内容),它仍然可能导致意外后果。如果MyApp已经依赖于Lib1,但它使用的是与Lib2所需的版本兼容的GAC / ngen副本,该怎么办?如果添加对Lib2的引用隐式为您创建了对Lib1的引用,则可能会更改加载了哪个Lib1并更改应用程序的运行时行为。它可能会检测到MyApp的bin目录中已经有一个Lib1并跳过它,但是它会假设已经存在的Lib1是正确的。也许这是一个陈旧的.dll等待被Clean操作擦掉而且覆盖是正确的举动。
NuGet解决了您使用包依赖性描述的问题。如果Lib1和Lib2都有nuget包并且Lib2包依赖于Lib1包,当你将Lib2添加到MyApp时,也会添加Lib1。两个pacakges的dll最终都会进入MyApp的bin目录。
最好的办法是反思你的想法。而不是思考:
想想:
答案 2 :(得分:0)
如果您有10个dll,则使用后构建事件进行复制会更容易:
xcopy "$(ProjectDir)bin\*.dll" $(SolutionDir)MyTargetProject\bin\" /y