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

时间:2013-11-29 07:45:32

标签: c# .net nuget-package class-library

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

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

问题是在运行时期间它抛出了一个未找到Google.Apis.dll的异常。此DLL是使用nuget下载的依赖项。

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

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

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

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

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

我只是过度思考了吗?

感谢

3 个答案:

答案 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目录中:

  1. 在项目Y中添加对程序集B的引用。
  2. 将虚拟代码添加到项目X中使用程序集B的文件中。
  3. 就个人而言,我个人更喜欢选项2。

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

          // 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目录。

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

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

想想:

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

答案 2 :(得分:0)

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

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