如果在解决方案中使用项目依赖项,则MSBuild不会复制引用(DLL文件)。

300
我在Visual Studio解决方案中有四个项目(都是针对.NET 3.5),但只有这两个对我的问题很重要:
1. MyBaseProject <- 这个类库引用了第三方DLL文件(elmah.dll) 2. MyWebProject1 <- 这个Web应用程序项目引用了MyBaseProject
我通过点击 "添加引用..." -> "浏览"选项卡 -> 选择"elmah.dll" 在 Visual Studio 2008 中将 elmah.dll 引用添加到 MyBaseProject。Elmah 引用的属性如下:
- 别名 - 全局 - 复制本地 - true - 文化 - - 描述 - 用于 ASP.NET 的错误日志记录模块和处理程序 (ELMAH) - 文件类型 - 程序集 - 路径 - D:\webs\otherfolder\_myPath__tools\elmah\Elmah.dll - 已解析 - True - 运行时版本 - v2.0.50727 - 指定版本 - false - 强名称 - false - 版本 - 1.0.11211.0
在 MyWebProject1 中,我通过 "添加引用..." -> "项目"选项卡 -> 选择"MyBaseProject" 来添加对 MyBaseProject 项目的引用。该引用的属性与上述相同,除了以下成员:
- 描述 - - 路径 - D:\webs\CMS\MyBaseProject\bin\Debug\MyBaseProject.dll - 版本 - 1.0.0.0
如果我在 Visual Studio 中运行构建,则 elmah.dll 文件将与MyBaseProject.dll一起复制到 MyWebProject1 的bin目录中!但是,如果我清理并通过 D:\webs\CMS> C:\WINDOWS\Microsoft.NET\Framework\v3.5\MSBuild.exe /t:ReBuild /p:Configuration=Debug MyProject.sln 来运行解决方案的 MSBuild,则在MyWebProject1的bin目录中缺少 elmah.dll——尽管构建本身没有警告或错误!
我已经确保 MyBaseProject 的 .csproj 包含具有值 "true" 的 private 元素 (这应该是 Visual Studio 中 "复制本地" 的别名) 。
<Reference Include="Elmah, Version=1.0.11211.0, Culture=neutral, processorArchitecture=MSIL">
  <SpecificVersion>False</SpecificVersion>
  <HintPath>..\mypath\__tools\elmah\Elmah.dll</HintPath>
    **<Private>true</Private>**
</Reference>

默认情况下,.csproj的xml中不会出现私有标签,尽管Visual Studio显示“复制本地”为true。我将“复制本地”设置为false,保存,然后再设置为true - 保存! MSBuild出了什么问题?如何才能让(elmah.dll)引用被复制到MyWebProject1的bin目录下?我不想在每个项目的后期构建命令中添加复制操作!(想象一下,如果有很多项目依赖于MyBaseProject!)

12
我希望您能给出更清晰的答案,解释为什么会发生这种情况。 - David Faivre
1
请查看此处提供的答案:https://dev59.com/UXNA5IYBdhLWcg3wPLL-#12076542 - Anuroopa Shenoy
1
有没有完整的源代码示例,可以作为最终解决方案? - Kiquenet
2
请查看@deadlydog在https://dev59.com/PXNA5IYBdhLWcg3wAIxP#21055664下的答案。他给出了非常好的解释并且解决了我的问题……下面得票最高的答案对于VS2012是不正确的。 - Jeff Widmer
2
请投赞成票:https://connect.microsoft.com/VisualStudio/feedback/details/652785/visual-studio-does-not-copy-referenced-assemblies-through-the-reference-hierarchy - Zack
就我所知,我认为这个属性在某个时候被NuGet删除了。 - Worthy7
20个回答

173

我通常这样处理。转到引用的属性并执行以下操作:

Set "Copy local = false"
Save
Set "Copy local = true"
Save

就是这样了。

Visual Studio 2010最初并没有在引用标签中添加:<private>True</private>,将"copy local"设置为false会导致它创建标签。之后它会根据需要将其设置为true或false。


10
这真是个天赐之物。谢谢你! - Rebecca
24
在使用MSBuild 4 / VS2012时,这对我没有起到作用。也就是说,我能够更新引用以添加<Private>true</Private>,但似乎对MSBuild没有影响。最终,我只需向旧版项目添加NuGet引用即可。 - Michael Teper
2
对我没用。它仍然没有将System.Net.Http.Formatting复制到bin文件夹中。 - Akira Yamamoto
7
这相当于“你尝试过关掉再重新启动吗?”,并且它奏效了! - FarFigNewton
6
看起来VS2015依然如故:将引用的.dll文件的 '复制本地副本' 属性从“False”设置为“True”,再改回“False”,这样做就能奏效。 - flip
显示剩余12条评论

172

我不确定为什么在使用Visual Studio和MsBuild构建时存在差异,但是当我在MsBuild和Visual Studio遇到这个问题时,下面是我发现的情况。

解释

假设我们有项目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);
    }

5
这里没有讨论的是,在Web项目的/bin文件夹中复制构建产品与常规复制到目标输出目录(例如/bin/x86/Debug)之间存在差异。前者是由被引用项目在构建时完成的,而后者是由依赖的Web项目完成的。检查Microsoft.Common.targets有助于理解这一点。复制到Web /bin的操作根本不依赖于本地复制行为 - 本地复制会影响到复制到输出目标目录,而该目录不是Cassini通过Debug运行时引用结构的一部分。 - user1164178
8
你能解释一下为什么在使用VS时不需要添加“虚拟代码”,但是在使用msbuild时却不能工作吗? - toebens
3
比调用函数更少侵入性的是,您可以将装配体中包含的类的类型分配给一个虚拟变量。Type dummyType = typeof(AssemblyA.AnyClass); - Arithmomaniac
17
除非您在Visual Studio中勾选了“优化代码”设置,否则上面展示的解决方法#2将有效。在这种情况下,它仍将排除dll。我添加了一行来覆盖其“优化”。 Console.WriteLine(dummyType.FullName); - JasonG
4
在Visual Studio 2017中,解决方案2对我无效。起初我以为是因为我的程序集X只使用了B中的一个“enum”并且我认为这个“enum”已经被内联了,所以我添加了直接使用B中的类型的代码,但是还是没用。同时,我的X一直在使用A中派生自B的类型,所以我不能理解编译器怎么会认为X不需要B并且可以忽略它。这太疯狂了。 - Xharlie
显示剩余9条评论

40

如果您在代码中没有直接使用汇编语言,那么Visual Studio会检测到它没有被使用并且不会将其包含在输出中,这是为了提高效率。我不确定您为什么在Visual Studio和MSBuild之间看到了不同的行为。您可以尝试将构建输出设置为两者的Diagnostic并比较结果,看看它们何时发生差异。

至于您的elmah.dll引用,如果您在代码中没有直接引用它,您可以将其添加为项目的一个项,并将构建操作设置为Content,并将复制到输出目录设置为Always


3
如果代码中未使用Elmah,那么将其作为内容复制是有道理的。给你的“复制到输出目录”评论点个赞。 - Kit Roed
4
确实,它会忽略未被使用的程序集。但是需要注意的一点是,在VS 2010中,使用XAML资源字典中的程序集并不被视为使用程序集,因此VS不会将其复制。 - Alex Burtsev
3
如果在XAML资源字典中使用引用项目中的程序集,MSBuild不会将它们作为依赖项包含在内。 - Alex Burtsev
这对于需要 dll 的单元测试项目更好。我不想添加主项目不需要的 dll,只是为了让测试运行! - Lukos

17

请看:

我在MSBuild论坛上发布的这个帖子

您将在那里找到我的临时解决方案/解决方法!

(MyBaseProject需要一些代码,引用elmah.dll中的某些类(无论是什么),以便elmah.dll被复制到MyWebProject1的bin目录中!)


1
该死 - 这也是我想到的唯一解决方案 - 希望有更好的方法来做到这一点! - nickspoon
2
请看安德鲁的回复,那里有一个更好的解决方案(无意冒犯!) - MPritchard
1
对于现在正在查看此内容的人,MSDN上toebens的答案基本上与deadlydog的答案相同,只是提供了一些年后。 - jpaugh

9

我有同样的问题。

请检查您项目所使用的框架版本是否与您引用的dll的框架版本相同。

在我的情况下,我的客户端是使用“Framework 4 Client”编译的,而DLL文件则是在“Framework 4”中。


8
我面临的问题是我有一个依赖于库项目的项目。为了构建,我按照以下步骤进行操作:
msbuild.exe myproject.vbproj /T:Rebuild
msbuild.exe myproject.vbproj /T:Package

当然,这意味着我在bin目录中和最重要的压缩包文件中丢失了库的dll文件。我发现以下方法可以完美解决此问题:
msbuild.exe myproject.vbproj /T:Rebuild;Package

我不知道为什么这样做有效或者一开始为什么没有效果。但是希望这可以帮到你。


1
在使用 TeamCity 一步构建整个解决方案并在下一步中对 WebAPI 项目进行 /t:Package 时,我遇到了相同的问题。任何项目引用的 dll 都没有被包含在内。通过使用上述方法 - 在 WebAPI 上执行 /T:Rebuild; Package,然后包含这些 dlls,问题已得到解决。 - Andy Hoyle

6
如评论中的Alex Burtsev所述,任何仅在XAML资源字典中使用的内容,在我的情况下,任何仅在XAML中而不是在代码后端中使用的内容,都不被MSBuild视为“正在使用”。 因此,只需在一些代码后面新建一个类/组件的虚拟引用即可使MSBuild相信该程序集实际上正在使用。

这正是我的问题,也是有效的解决方案。花了太长时间来尝试解决这个问题。感谢Scott和@Alex Burstev。 - kay
真是让我抓狂了。没错,我正在使用FontAwesome.WPF,并且仅在XAML中使用(出于显而易见的原因)。添加一个虚拟方法有所帮助。谢谢!还有,VS 2017 15.6仍然受到影响,所以我提交了一个错误报告:https://github.com/dotnet/roslyn/issues/25349 - Sören Kuklau
似乎这个问题在 .NET Core 3.0 预览版和 .NET 5 之间得到了解决。有人可以确认一下吗? - Sören Kuklau

6

我曾经遇到完全相同的问题,原因是解决方案中的2个项目引用了不同版本的第三方库。

一旦我纠正了所有引用,一切都正常工作了。


5
使用deadlydog的方案,我的问题是在我构建Y时,来自X的程序集(A和B共15个)没有出现在Y的bin文件夹中。
通过从Y中删除对X的引用,保存,构建,然后重新添加X引用(作为项目引用),保存,构建,A和B开始出现在Y的bin文件夹中。

在搜索了许多解决方案并尝试了很多之后,这个对我起作用了。 - Suncat2000
在比较 csproj 文件之前和之后进行差异分析会很有趣。 - StayOnTarget

4

将目标框架从.NET Framework 4 Client Profile更改为.NET Framework 4,可以解决我的问题。

因此,在您的示例中:将MyWebProject1的目标框架设置为.NET Framework 4


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接