【问题标题】:Is "Copy Local" transitive for project references?项目引用的“复制本地”是否可传递?
【发布时间】:2014-11-27 14:13:05
【问题描述】:

写。提议的骗子:由于这里的问题与linked question相反,我宁愿认为它不是骗子。

首先,我确实阅读了What is the best practice for “Copy Local” and with project references?(也有this),无论如何我都必须尝试一下,但是获得对此的一般反馈似乎是必要的,因为docs 对此东西太可怕了,我只在 VS2010 上,也许他们在新版本中改变了一些东西,很高兴知道。

第二,我只对这个问题的 project 参考感兴趣,因为我已经 read that assemblies from the GAC are handled differently 和 GAC 无关我的问题。

第三,在阅读了建议的欺骗之后,但更重要的是 @Albireo 在这里的 answer,区分 file 依赖项似乎也很重要,其中依赖项引用了一个 dll 程序集文件和 project 依赖项(即我要问的),其中依赖项引用了一个 project 并隐式地引用了该项目的输出文件.

无论如何,情况就是这样,我觉得有点奇怪,但仍然:

  • 2 个 C# 可执行项目
  • n C# dll 程序集项目
  • 这 2 个可执行文件具有不同的输出目录,因为它们将单独部署,因此它们在开发人员计算机上也是独立的
  • 这 2 个可执行文件依赖于某些 DLL 程序集(它们可能相互依赖)
  • 共有三个输出目录:
    • /x1 用于可执行 1 项目
    • /x2 用于可执行 2 项目
    • /lib 用于所有 dll 程序集

DLL 程序集所有都将Copy Local设置为false,用于它们的项目引用,因为它们都构建到相同的输出目录。

这2个可执行项目已将它们直接引用的所有DLL程序集项目引用的Copy Local设置为true,以便将DLL复制到@分别为 987654336@/x2

问题现在是 wrt。到可执行项目直接引用但通过引用程序集传递的DLL:Will程序集,当“复制本地”在第一个程序集上设置为 true 时,通过另一个程序集传递引用,复制到可执行文件的输出文件夹

例子:

  • x1.csproj(例如输出 = x1/one.exe
    • 参考:dlA.csproj(例如输出 = lib/a.dll)和Copy Local = *true*
    • (没有直接引用 b.dll)
  • dlA.csproj(例如输出 = lib/a.dll
    • 参考:dlB.csproj(例如输出 = lib/b.dll)和Copy Local = **false**
    • (没有直接引用 c.dll)
  • dlC.csproj(例如输出 = lib/c.dll
    • (没有进一步的相关参考资料)

因此,我们有一个one.exe -> a.dll -> b.dll -> c.dll 的逻辑依赖关系,其中只有a.dll 显然被复制到one.exe 的输出目录。 其他两个 dll 是否也会被复制到输出目录? 这是否记录在某个地方?


而且,是的,我试过了。而且,是的,它似乎可以工作,但我还没有足够努力地戳它,无论如何,我可能错过了更多的东西。 (还有任何官方文档的问题。)

【问题讨论】:

  • @Albireo - 来自您提议的欺骗:“所有项目引用都具有 CopyLocal = true。” ...我确实没有有这个。所以这不可能是一个欺骗问题?
  • 我在使用 AutoMapper 时遇到了同样的问题,它使用 .NET 4 代码的附属程序集。 AutoMapper 在类库项目中被引用,并且在构建我们的 Web 应用程序时,从不复制附属程序集除非我们在类库项目中使用了它的一些代码。
  • 问题在于Visual Studio如何计算依赖图,Copy Local设置对这个问题没有影响。当我遇到这个问题时,我发现了一个详细的帖子来解释这个问题,但我现在找不到它,这个问题是我能找到的更接近的东西。
  • 嗯...记录了一个ResolveAssemblyReference 任务。也许进一步研究可以对此有所了解......

标签: c# .net visual-studio msbuild copy-local


【解决方案1】:

似乎区分文件依赖项也很重要,其中依赖项引用 dll 程序集文件和项目依赖项(即我要问的),其中依赖项引用项目并隐含地输出该文件项目。

不是真的,不。

MSBuild 并不真正关心引用是指向解决方案中的另一个项目还是指向 DLL。

如果 ProjectA 依赖于 ProjectB 来构建 ProjectA ProjectB 必须已经构建(并且是最新的),那么 MSBuild 将提取其 DLL(而不是其 C# 代码)并将其链接到 @ 987654325@.

为方便起见,添加项目引用而不是 DLL 是“语法糖”:这样 MSBuild 知道它必须选择引用项目的输出,无论输出是什么。

否则,您将不得不手动预构建依赖项,找到它的 DLL 并将其链接到项目,每当您切换构建配置、移动或重命名时都重复该过程。不太实用。

另外两个dll也会复制到输出目录吗?

如果直接从引用程序集的项目中使用依赖项中的任何类型的元素,则将复制该引用。

一个例子可能是这个解决方案布局:

  • 我的解决方案
  • MySolution.ConsoleApplication
  • MySolution.FirstDependency
  • MySolution.SecondDependency
  • MySolution.ThirdDependency
  • MySolution.FourthDependency

有了这个依赖链:

  • MySolution.ConsoleApplication
  • MySolution.FirstDependency
    • MySolution.SecondDependency
      • MySolution.ThirdDependency
      • MySolution.FourthDependency

如果您构建此解决方案,您会注意到在MySolution.ConsoleApplication 输出目录中将有MySolution.FirstDependencyMySolution.SecondDependencyMySolution.ThirdDependency 的DLL,但没有MySolution.FourthDependency 的DLL。

为什么会这样? 当 MSBuild 构建 MySolution.SecondDependency 时,它注意到有一个声明为 MySolution.FourthDependency 的依赖项,但是由于它在 MySolution.SecondDependency 代码中找不到来自 MySolution.FourthDependency 的任何元素的任何用法,它决定执行一些“优化”并从输出中省略 MySolution.FourthDependency 程序集

过去,当我通过 NuGet AutoMapper 添加到“深度依赖项”时,同样的问题困扰着我:添加 AutoMapper 会添加两个程序集引用,AutoMapperAutoMapper.Net4,其中第二个程序集由第一个程序集通过反射加载当它需要对 .NET Framework 4 引入的新集合对象执行某种操作时。由于第二个程序集是通过反射加载的,MSBuild 认为它没有被使用,因此不必费心复制它。

所以,是的,只要您直接使用它们,它们就会被复制,而不是通过反射。

这是否记录在某处?

这种行为似乎是 MSBuild 的一个“特性”,当我遇到这个问题时,我设法找到了 Microsoft 一些人的博客文章,但现在我找不到了。

【讨论】:

  • 我知道这有点多云,如果有更熟练的人想澄清我的解释,请随时这样做。
  • 非常感谢您的回答。谢谢!但是,我不认为您对 Automapper 的问题是针对程序集文件依赖项,而我的问题是关于程序集项目依赖项。它们在 msbuild 中是不同的。我将把它添加到问题中。
  • 非常感谢。我冒昧地强调了让我接受答案的要点
  • IoC 的解决方案是什么?我有同样的问题,通过添加一个愚蠢的用法部分来解决它......它自己很愚蠢。
  • @nocgod IoC 应该与此问题无关,除非您动态加载程序集,因为您的 IoC 容器所在的层应该直接引用它能够注入的所有内容。但是,我知道解决此问题的唯一两种方法是在构建后事件中进行虚假使用或手动 XCOPY。
【解决方案2】:

它非常简单,与复制本地没有任何关系。 MSBuild 在程序集的元数据中查找程序集的依赖项。你也可以在程序集上运行 ildasm.exe 并双击 Manifest。一定要试试这个以获得洞察力。您将看到 .assembly 指令。由编译器在构建程序集时插入,只会列出您在代码中实际使用的引用程序集。

如果 MSBuild 可以在同一目录中找到这样的程序集,那么它将自动复制它。如果没有,那么它将静默跳过副本。

由此,您可以推断出故障模式。它不能复制非托管 DLL,它们不会出现在元数据中。它无法通过 Assembly.Load/From() 复制您间接依赖的程序集,它们也不会出现在元数据中。它无法复制尚未构建的程序集,这是构建顺序问题。它不能复制您设置为 False 的 Copy Local 属性的程序集。如果程序集存在于 GAC 中,这通常是一个有效的选择,不需要副本。

对于需要帮助的这种情况,构建后事件中的 XCOPY 可以完成工作。

【讨论】:

  • "它不能复制您将 Copy Local 属性设置为 False 的程序集。" ...但这与元数据没有任何关系?
  • 不,这会调用“它将默默地跳过副本”子句。我知道元数据的依赖关系,但文件不存在。
  • BTW - 非托管 DLL可能是程序集的一部分,在这种情况下,它们将在元数据 (MSIL .file) 中被引用。除了 GAC 工具之外,对复制此类程序集的支持还很粗略。
  • 感谢.manifest 链接/复制见解!现在明白为什么我们需要添加一些未调用的代码来使用特定类型了!
  • "如果 MSBuild 可以在同一目录中找到这样的程序集,那么它会自动复制它。如果没有,它会默默地跳过复制" 不适合我。它正在各个地方寻找它,例如从这里 C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\System.Runtime.CompilerServices.Unsafe.dll,但是它也会检查其他文件夹。如果它没有在任何这些已知文件夹中找到间接引用程序集,它将跳过它。但是这种行为非常令人困惑并且容易出错(并且不是确定性的)。我对此有很多问题。
猜你喜欢
  • 2021-05-20
  • 2010-10-10
  • 1970-01-01
  • 2021-04-05
  • 2012-07-08
  • 2015-02-17
相关资源
最近更新 更多