类库中的引用不会复制到正在运行的项目bin文件夹中

我有一个代表我的逻辑层的类库。 对于那个库,我为Google.Apis.Analytics.v3添加了一个nuget包 – 它安装了包及其所有依赖项。

我有一个控制台应用程序,它使用该逻辑类库(常规引用)。 一切都写得很好。

问题是在运行时它抛出了一个exception,即找不到Google.Apis.dll。 此DLL是与nuget一起下载的依赖项。

检查BIN文件夹,我发现在类库bin文件夹中存在此DLL,但在控制台应用程序BIN文件夹中却没有(而其他相关的DLL是)。 所以这意味着在编译期间不是所有引用都被复制了。

我在网上搜索过,找到了所有不起作用的解决方法(比如手动编辑项目文件并删除该dll定义中的真正xml行)。

我最终做的是在我的控制台应用程序中添加相同的nuget库 – 它可以工作,但感觉有点脏,而不是应该的方式。 我认为控制台应用程序是客户谁应该从该逻辑类库获取它的服务,应该知道它的东西没有“客户”担心它。

此外,该控制台应用程序不是唯一一个将使用该服务的人,我也计划使用该function的网络应用程序 – 所以我需要将相同的nuget添加到该Web应用程序 – 再次,感觉有点乱……

只有我吗? 这是正确的方法吗? 我正在考虑编写一个WCF项目来处理这个function – 但这似乎只是在function方面的一些开销,并且可能会减慢我的工作流程,以便在我看来保持“更清洁”。

我只是过度思考了吗?

谢谢

说明

对于示例场景,假设我们有项目X,程序集A和程序集B.程序集A引用程序集B,因此项目X包含对A和B的引用。此外,项目X包含引用程序集A的代码(例如A. SomeFunction())。 现在,您创建一个引用项目X的新项目Y.

所以依赖链看起来像这样: Y => X => A => B.

Visual Studio / MSBuild试图变得聪明,只将引用引入它检测为项目X所需的项目Y; 这样做是为了避免项目Y中的参考污染。问题是,由于项目X实际上不包含任何明确使用程序集B的代码(例如B.SomeFunction()),VS / MSBuild不会检测到B是必需的通过X,因此不会将其复制到项目Y的bin目录中; 它只复制X和A程序集。

您有两个选项可以解决此问题,这两个选项都会导致程序集B被复制到项目Y的bin目录中:

  1. 在项目Y中添加对程序集B的引用。
  2. 将虚拟代码添加到项目X中使用程序集B的文件中。

我个人更喜欢选项2,原因有两个。

  1. 如果您将来添加另一个引用项目X的项目,则不必记住还包括对程序集B的引用(就像您必须使用选项1一样)。
  2. 你可以有明确的评论说明为什么虚拟代码需要存在而不是删除它。 因此,如果有人确实删除了代码(比如使用重构工具查找未使用的代码),您可以从源代码控制中轻松地看到代码是必需的并恢复它。 如果您使用选项1并且某人使用重构工具来清理未使用的引用,则您没有任何注释; 您将看到从.csproj文件中删除了引用。

这是我在遇到这种情况时通常会添加的“虚拟代码”示例。

  // 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); } 

如果您有以下依赖关系链: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(并且不仅仅复制bin目录内容)时为您提供了更聪明且隐式引用的Lib1,则仍可能导致意外后果。 如果MyApp已经依赖于Lib1,但它使用的是与Lib2所需的副本兼容的GAC’d / ngen副本,该怎么办? 如果添加对Lib2的引用隐式为您创建了对Lib1的引用,那么可能会更改加载了哪个Lib1并更改了应用程序的运行时行为。 它可能会检测到MyApp的bin目录中已经有一个Lib1并跳过它,但是它会假设已经存在的Lib1是正确的。 也许这是一个陈旧的.dll等待被Clean操作擦掉而且覆盖是正确的举动。

NuGet解决了您使用包依赖性描述的问题。 如果Lib1和Lib2都有nuget包并且Lib2包依赖于Lib1包,那么当你将Lib2添加到MyApp时,也会添加Lib1。 两个pacakges的dll最终都会进入MyApp的bin目录。

最好的办法是反思你的想法。 而不是思考:

  • Lib2需要引用Lib1才能编译,所以我将添加对Lib1的引用

认为:

  • MyApp需要Lib2。 无论Lib2需要什么,我都需要。 所以MyApp和Lib2都得到了对Lib1的引用。

如果您有10个dll,则更容易使用postbuild事件进行复制:

 xcopy "$(ProjectDir)bin\*.dll" $(SolutionDir)MyTargetProject\bin\" /y