【问题标题】:MSBuild doesn't copy references (DLL files) if using project dependencies in solution如果在解决方案中使用项目依赖项,MSBuild 不会复制引用(DLL 文件)
【发布时间】:2010-11-11 01:27:27
【问题描述】:

我的 Visual Studio 解决方案中有四个项目(每个人都针对 .NET 3.5) - 对于我的问题,只有这两个很重要:

  1. MyBaseProject
  2. MyWebProject1

我在 Visual Studio 2008 中添加了对 MyBaseProject 的 elmah.dll 引用,方法是单击“添加引用...”→“浏览”选项卡→选择“elmah.dll”。

Elmah 引用的属性如下:

  • 别名 - 全局
  • 复制本地 - 真
  • 文化-
  • 描述 - 用于 ASP.NET 的错误记录模块和处理程序 (ELMAH)
  • 文件类型 - 程序集
  • 路径 - D:\webs\otherfolder\_myPath\__tools\elmah\Elmah.dll
  • 已解决 - 是的
  • 运行时版本 - v2.0.50727
  • 指定版本 - 错误
  • 强名称 - 错误
  • 版本 - 1.0.11211.0

MyWebProject1 中,我通过以下方式添加了对 Project MyBaseProject 的引用: “添加参考...”→“项目”选项卡→选择“MyBaseProject”。除了以下成员之外,此引用的属性相同:

  • 说明 -
  • 路径 - D:\webs\CMS\MyBaseProject\bin\Debug\MyBaseProject.dll
  • 版本 - 1.0.0.0

如果我在 Visual Studio 中运行构建,elmah.dll 文件将与 MyBaseProject.dll 一起复制到我的 MyWebProject1 的 bin 目录中!

但是,如果我为解决方案清理并运行 MSBuild(通过 D:\webs\CMS> C:\WINDOWS\Microsoft.NET\Framework\v3.5\MSBuild.exe /t:重建 /p:Configuration=Debug MyProject.sln) MyWebProject1 的 bin 目录中的 elmah.dll 丢失 - 尽管构建本身不包含警告或错误!

我已经确保 MyBaseProject 的 .csproj 包含值为“true”的 private 元素(这应该是 Visual 中“copy local”的别名工作室):

<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 说“复制本地”为真。我将“复制本地”切换为假 - 已保存 - 并再次将其设置回真 - 保存! )

MSBuild 出了什么问题?如何将 (elmah.dll) 引用复制到 MyWebProject1 的 bin?

我不想在每个项目的 postbuild 命令中添加 postbuild 复制操作! (想象一下我会有很多项目依赖于 MyBaseProject!)

【问题讨论】:

标签: visual-studio msbuild reference dependencies project


【解决方案1】:

我只是这样处理它。转到参考的属性并执行以下操作:

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

就是这样。

Visual Studio 2010 最初并没有把: &lt;private&gt;True&lt;/private&gt; 在引用标签中并将“本地复制”设置为 false 会导致它创建标签。之后它将相应地设置为 true 和 false。

【讨论】:

  • 这是天赐之物。谢谢你!
  • 不适用于 MSBuild 4 / VS2012。也就是说,我能够将引用更新为&lt;Private&gt;true&lt;/Private&gt;,但它似乎对 MSBuild 没有影响。最后,我只是添加了对下级项目的 NuGet 引用。
  • 对我不起作用。它仍然没有将 System.Net.Http.Formatting 复制到 bin 文件夹中。
  • 这相当于Have you tried turning it off and on again?,而且它有效!
  • 看起来 VS2015 的行为仍然相同:将 'Copy Local' 设置为 'False' 然后在引用的 .dll 上设置回 'True' 有效。
【解决方案2】:

我不确定为什么在 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 试图变得聪明,只将引用引入项目 Y,它检测到项目 X 需要它;它这样做是为了避免项目 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. 您可以让 cmets 明确说明为什么需要存在虚拟代码而不是删除它。因此,如果有人不小心删除了代码(例如使用查找未使用代码的重构工具),您可以轻松地从源代码控制中看到需要该代码并恢复它。如果您使用选项 1 并且有人使用重构工具清理未使用的引用,则您没有任何 cmets;您只会看到从 .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);
    }

【讨论】:

  • 这里没有讨论的是,在 web 项目的 /bin 中复制构建产品和常规复制到目标输出目录(例如 /bin/x86/调试)。前者在构建时由引用的项目完成,后者由依赖的 Web 项目完成。检查 Microsoft.Common.targets 有助于理解这一点。复制到 web /bin 根本不依赖于复制本地行为 - 复制本地对副本的影响到输出目标目录,该目录不是 Cassini 通过调试运行时引用的结构的一部分。
  • 你能解释一下为什么它可以与 VS 一起工作而没有添加那个“虚拟代码”但不能与 msbuild 一起工作吗?
  • 甚至比调用函数的侵入性更小,您可以将程序集中包含的类的类型分配给虚拟变量。 Type dummyType = typeof(AssemblyA.AnyClass);
  • 如上所示的解决方案 #2 将起作用,除非您在 Visual Studio 中选中了“优化代码”设置。在这种情况下,它仍然会排除 dll。我又添加了一行来覆盖它的“优化”。 Console.WriteLine(dummyType.FullName);
  • 解决方案 2 在 Visual Studio 2017 中对我不起作用。我首先认为这是因为在我的程序集 X 中,我只使用了来自 B 的 enum 并且我认为枚举是内联的.我添加了代码以直接使用 B 中的类型,但这没有帮助。我的 X 在 A 中使用类型在 B 中的子类类型也是如此,所以我无法理解编译器如何认为 B 不是 X 所必需的并且可以被忽略。这太疯狂了。
【解决方案3】:

如果您没有直接在代码中使用程序集,那么 Visual Studio 在尝试提供帮助时会检测到它没有被使用并且不会将其包含在输出中。我不确定为什么您会在 Visual Studio 和 MSBuild 之间看到不同的行为。您可以尝试将构建输出设置为对两者都进行诊断,然后比较结果以查看差异所在。

至于您的 elmah.dll 引用,如果您没有直接在代码中引用它,您可以将其作为项目添加到您的项目中,并将 Build Action 设置为 Content 并将 Copy to Output Directory 设置为 Always

【讨论】:

  • +1 用于复制到输出目录注释,如果代码中没有使用 Elmah,则作为内容复制是有意义的。
  • 确实它忽略了未使用的程序集,但需要注意的一件主要事情是,从 VS 2010 开始,在 XAML 资源字典中使用程序集不被 VS 视为使用程序集,因此它不会复制它。
  • 这对于我需要 dll 的单元测试项目来说更好。我不想添加主项目不需要的 dll 来运行测试!
【解决方案4】:

看看:

This MSBuild forum thread I started

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

(MyBaseProject 需要一些代码从 elmah.dll 中引用一些类(无论如何),以便将 elmah.dll 复制到 MyWebProject1 的 bin!)

【讨论】:

  • 该死 - 这也是我找到的唯一解决方案 - 希望可能有更好的方法!
  • 请参阅下面的 andrew 的回复,了解一个不那么老套的解决方案(无意冒犯!)
  • 对于现在看这个的人来说,MSDN 上的toebens' answer 与几年后提供的deadlydog's answer 基本相同。
【解决方案5】:

我遇到了同样的问题。

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

就我而言,我的客户端是使用“Framework 4 Client”编译的,而 DLL 位于“Framework 4”中。

【讨论】:

    【解决方案6】:

    我面临的问题是我有一个依赖于库项目的项目。为了构建,我按照以下步骤操作:

    msbuild.exe myproject.vbproj /T:Rebuild
    msbuild.exe myproject.vbproj /T:Package
    

    这当然意味着我在 bin 中丢失了我的库的 dll 文件,最重要的是在包 zip 文件中。我发现这非常有效:

    msbuild.exe myproject.vbproj /T:Rebuild;Package
    

    我不知道为什么会这样,或者为什么一开始就没有。但希望这会有所帮助。

    【讨论】:

    • 我在使用 TeamCity 的 /t:Build 一步构建整个解决方案时遇到了同样的问题,然后在 WebAPI 项目的下一个 /t:Package 中构建了整个解决方案。不包括任何项目引用引用的任何 dll。这已使用上述方法修复 - /T:Rebuild;WebAPI 上的包然后包含这些 dll。
    【解决方案7】:

    我刚刚遇到了完全相同的问题,结果证明是由于同一解决方案中的 2 个项目引用了不同版本的 3rd 方库。

    一旦我更正了所有参考资料,一切都会完美运行。

    【讨论】:

      【解决方案8】:

      正如 Alex Burtsev 在评论中提到的那样,任何仅在 XAML 资源字典中使用的内容,或者在我的情况下,任何仅在 XAML 中使用而不在后面的代码中使用的内容,都不会被 MSBuild 视为“正在使用”。

      因此,只需在后面的一些代码中新建一个对程序集中的类/组件的虚拟引用,就足以让 MSBuild 确信该程序集实际上正在使用中。

      【讨论】:

      • 这正是我的问题和有效的解决方案。花了太长时间试图弄清楚这一点。谢谢斯科特和@Alex Burstev
      • 天哪,这让我发疯了。 是的,我使用的是 FontAwesome.WPF,并且来自 XAML(原因很明显)。添加一个虚拟方法有帮助。谢谢!是的,VS 2017 15.6 仍然受到影响,所以我提交了一个错误:github.com/dotnet/roslyn/issues/25349
      【解决方案9】:

      使用 deadlydog 的方案,

      Y => X => A => B,

      我的问题是当我构建 Y 时,来自 X 的程序集(A 和 B,全部 15 个)没有显示在 Y 的 bin 文件夹中。

      我通过从 Y 中删除引用 X、保存、构建,然后重新添加 X 引用(项目引用)来解决它,然后保存、构建,并且 A 和 B 开始出现在 Y 的 bin 文件夹中。

      【讨论】:

      • 经过数小时的搜索和尝试许多其他解决方案后,这个对我有用。
      • 比较一下 csproj 文件前后的差异会很有趣
      【解决方案10】:

      将目标框架从 .NET Framework 4 Client Profile 更改为 .NET Framework 4 为我解决了这个问题。

      所以在您的示例中:将 MyWebProject1 上的目标框架设置为 .NET Framework 4

      【讨论】:

        【解决方案11】:

        我遇到了同样的问题,并且 dll 是动态加载的引用。 为了解决这个问题,我在 dll 的命名空间中添加了一个“使用”。 现在 dll 被复制到输出文件夹中。

        【讨论】:

          【解决方案12】:

          这需要将.targets 文件添加到您的项目并将其设置为包含在项目的包含部分中。

          请参阅my answer here 了解程序。

          【讨论】:

            【解决方案13】:

            在构建期间引用未使用的程序集不是正确的做法。您应该扩充您的构建文件,以便复制其他文件。通过使用构建后事件或更新属性组。

            一些例子可以在其他帖子中找到

            【讨论】:

              【解决方案14】:

              出现这种情况的另一种情况是,如果您在 Visual Studio 中使用较旧的“网站”项目类型。对于该项目类型,它无法引用它自己的目录结构(当前文件夹和下)之外的 .dll。因此,在上面的答案中,假设您的目录结构如下所示:

              其中ProjectX和ProjectY是父/子目录,ProjectX引用A.dll,而A.dll又引用B.dll,而B.dll在目录结构之外,比如根目录下的Nuget包(Packages),然后将包含 A.dll,但不会包含 B.dll。

              【讨论】:

                【解决方案15】:

                我今天遇到了类似的问题,这肯定不是您问题的答案。但我想通知大家,并可能提供一些见解。

                我有一个 ASP.NET 应用程序。构建过程设置为清理然后构建。

                我有两个 Jenkins CI 脚本。一种用于生产,一种用于暂存。我将我的应用程序部署到登台并且一切正常。已部署到生产环境,但缺少引用的 DLL 文件。这个 DLL 文件就在项目的根目录中。不在任何 NuGet 存储库中。 DLL 设置为do not copy

                两个部署之间的 CI 脚本和应用程序相同。在暂存环境中清理和部署之后,DLL 文件仍被替换为 ASP.NET 应用程序的部署位置 (bin/)。生产环境并非如此。

                事实证明,在测试分支中,我在构建过程中添加了一个步骤,将这个 DLL 文件复制到 bin 目录。现在是花了一点时间才弄清楚的部分。 CI 过程没有自行清理。 DLL 留在工作目录中,并且意外地与 ASP.NET .zip 文件打包在一起。生产分支从未以相同的方式复制 DLL 文件,也从未意外部署过它。

                TLDR;检查并确保您知道构建服务器在做什么。

                【讨论】:

                  【解决方案16】:

                  确保两个项目都在同一个 .net 版本中,同时检查复制本地属性,但这应该是 true 作为默认值

                  【讨论】:

                    【解决方案17】:

                    使用 Visual Studio 2015 添加附加参数

                    /deployonbuild=false

                    到 msbuild 命令行修复了这个问题。

                    【讨论】:

                      【解决方案18】:

                      我刚刚遇到了一个非常相似的问题。使用 Visual Studio 2010 编译时,DLL 文件包含在 bin 文件夹中。但是使用 MSBuild 编译时,没有包含第三方 DLL 文件。

                      非常令人沮丧。我解决它的方法是在我的 Web 项目中包含对包的 NuGet 引用,即使我没有直接在那里使用它。

                      【讨论】:

                      • 这是最佳答案的副本。
                      【解决方案19】:

                      我认为@deadlydog 的答案对于当前的 Nuget 系统无效。我在 Visual Studio 2022 中使用 Y => X => A => B 重新创建了场景,我所要做的就是在终端中运行命令

                      msbuild -t:clean,rebuild,pack
                      

                      【讨论】:

                        【解决方案20】:

                        在网站项目中包含来自您的 projectreferences 的所有引用的 DLL 文件并不总是一个好主意,尤其是当您使用 dependency injection 时:您的 Web 项目只是想添加对接口 DLL 文件/项目的引用,而不是任何具体的实现 DLL 文件。

                        因为如果您直接添加对实现 DLL 文件/项目的引用,则无法阻止开发人员在实现 DLL 文件/项目的具体类上而不是通过接口调用“新”。这也是您在您的网站中声明了一个“硬代码”以使用该实现。

                        【讨论】:

                          猜你喜欢
                          • 2016-06-25
                          • 2020-01-02
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2013-12-22
                          • 1970-01-01
                          • 2018-11-02
                          • 2012-12-04
                          相关资源
                          最近更新 更多