默认情况下将“复制本地副本”设置为false?

39

我可以将Visual Studio中"复制本地"的默认选项设置为False吗?大多数情况下,当我将dll作为项目的依赖项添加时,我希望将Copy Local属性设置为False。默认情况下,它是True。是否有一种方法可以更改Visual Studio的默认行为?(2008)


1
有关使用MSBuild v15轻松解决此问题,请参见下面的我的答案:https://dev59.com/vnA75IYBdhLWcg3wy8Xr#50755479 - gReX
6个回答

38

不会 - Visual Studio使用一组内部规则来确定Copy Local的设置。

MSDN

  1. 如果引用是另一个项目,称为项目对项目引用,则该值为true
  2. 如果程序集在全局程序集缓存中找到,则该值为false
  3. 作为特殊情况,mscorlib.dll引用的值为false
  4. 如果程序集在Framework SDK文件夹中找到,则该值为false
  5. 否则,该值为true

1
MSDN链接中的内容已被撤销。 - Oram

29

实际上,您可以。您需要以下几点:

  1. 创建 .targets 文件,并将 copylocal (<Private> 标签,确切地说) 默认设置为false
  2. .csproj 文件中导入该目标。您可以将其添加在最后一行之前,关闭 </Project> 标记之前,它看起来像 <Import Project="..\Build\yourtarget.targets" />

现在,每个具有此目标的项目都默认禁用了copylocal。

缺点是您需要修改每个csproj文件,包括新项目。您可以通过修改VS项目模板解决新项目问题。与博客文章中描述的 Class.cs 不同,您需要修改的是 Class.vstemplate(在相同的zip文件中)。

使用这种方法,还有一个问题 - 路径本身。如果在新生成的csproj文件中使用硬编码的相对路径,则可能会出现错误(除非您具有平面项目结构)。

您可以:

  • 使VS生成正确的相对路径。不确定如何实现这一点,甚至是否可能。
  • 忽略它,并为每个新csproj手动更改路径(虽然不是理想的解决方案,但这可能是可以容忍的,具体取决于您有多少新项目)。
  • 使用环境变量而不是相对路径。在这种情况下,每个开发人员都需要设置相同的变量。

肯定有更好的解决方法,但我还没有找到。


这似乎是正确的答案,我遵循了指导和链接,它有效! - Anthony Mastrean
@AnthonyMastrean:你有没有想到一个聪明的方法来将import放入新创建的csproj文件中? - ya23
我们正在使用一个常见的.csproj文件,通过模板导入到每个新项目中。模板系统不是很干净,我们还不确定是否喜欢它。 - Anthony Mastrean
1
你可以使用dir /s /b *csproj > project-files.txt列出根目录下的每个项目文件,然后使用for /f %i in (project-files.txt) do notepad++ "%i"打开它们。由于notepad++允许您在所有打开的文件中进行替换,您可以要求它使用正则表达式将^</Project>替换为<Import Project="$\(SolutionDir\)customTarget.targets /></Project>。然后,您只需要在.sln文件旁边创建一个名为customTarget.target的targets文件即可。我想尝试这个方法,但入门门槛似乎很高(需要修改每个csproj!),但是这个方法突然间就想到了我... - Tim Barrass

12

从 msbuild v 15 开始,您可以在包含源代码的根文件夹中复制一个名为Directory.Build.props的单个文件:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemDefinitionGroup>
  <Reference>
    <Private>False</Private>
  </Reference>
  <ProjectReference>
     <Private>False</Private>
  </ProjectReference>
</ItemDefinitionGroup>
</Project>

已经没有其他事情要做了!这在Visual Studio 2017以及vNext Build中运作良好。您可能需要关闭Visual Studio,然后重新打开解决方案才能生效。

https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-your-build#directorybuildprops-and-directorybuildtargets


这是仅适用于 .NET Core 项目的方法吗?还是您也可以在旧的 .NET 项目中使用,例如 .NET 452? - Dave Barnett
从阅读Glen Slayden的答案来看,也包括<ProjectReference> <Private>False</Private> </ProjectReference>不是更好吗? - Dave Barnett
@Dave Barnett 你说得对,如果这也适用于ProjectReferences的话。你试过了吗?如果你愿意,你可以在我的回答中添加这个标签。我在我的项目中没有使用Project References。我在.Net 4.7.1和TFS 2018以及Visual Studio Enterprise 2017上检查了使用dll-References的解决方案。 - gReX
是的,我在一个 .net 4.5.2 项目上尝试了它。这个项目使用了项目引用,显然在我意识到需要使用项目引用标签之前是无法工作的,但是一旦使用了该标签,它就可以工作了。这是一个非常好的解决方案,因为不需要编辑任何现有文件。我已经添加了该标签并等待同行审查。 - Dave Barnett

8

我们不使用.targets文件(如ya23的答案中建议的那样),所以我们只是在文本编辑器中手动编辑.csproj项目文件,并在引用中添加<Private>元素,就像这样:

<Reference Include="[...]">
  <Private>False</Private>
  [...]
</Reference>

<Private> 元素的值与“复制本地”属性的值相匹配。例如,如果 <Private> 设置为 False,则“复制本地”也为 False。


4

关于@herzbube发布的解决方案,如果您想关闭在.csproj文件中的所有(或大多数)引用的“复制本地”功能,则无需在每个Reference上单独设置<Private>False</Private>,您可以直接将以下内容放入.csproj中:

<ItemDefinitionGroup>
  <Reference>
    <Private>False</Private>
  </Reference>
</ItemDefinitionGroup>

这不会影响使用<ProjectReference>引用的项目,但您可以为这些项目执行同样的操作——要么替代,要么一并执行:

<ItemDefinitionGroup>
  <ProjectReference>
    <Private>False</Private>
  </ProjectReference>
</ItemDefinitionGroup>

如果您想要这两个操作,您可以将它们合并为一个组:
<ItemDefinitionGroup>
  <Reference>
    <Private>False</Private>
  </Reference>
  <ProjectReference>
    <Private>False</Private>
  </ProjectReference>
</ItemDefinitionGroup>

确保在第一个实际的 <Reference … ><ProjectReference … > 标签之前放置这些覆盖,因为这些块只适用于出现在它们下面的引用。然后,如果有一些你确实想要本地复制的内容,你可以单独覆盖这些内容(即在各自的标签内部),这次使用 True
对于更高级的情况,你可以在同一个 .csproj 文件中多次在 TrueFalse 之间切换覆盖值。另一种高级技术是将一些引用策略性地放在这些块下面,而将其他引用放在上面,这样后者就不会受到影响。
所有这些都应该使你的 .csproj 中的 XML 更加清洁和易于阅读。但还有更多好消息,请继续阅读...
至于选择哪些项目应该被标记为<Private>False</Private>,这通常取决于具体情况,但对于初学者来说,每个使用默认(即每个项目本地)C#输出位置的大型解决方案几乎总是应该采取以下步骤:
在任何一个构建多个 C# 类库的 Visual Studio 解决方案中,如果这些类库具有任何非平凡数量的 <ProjectReference> 相互依赖,并且最终构建一个或多个应用程序(即可执行文件):
1. 在每个类库的 .csproj 文件顶部附近,插入上面显示的 <ProjectReference> 块。 原因:没有必要将任何 .dll 收集到其自己的子目录中,因为从该位置永远不会运行任何可执行文件。这种繁琐的复制只是无用的忙碌工作,并可能不必要地减慢您的构建速度,可能相当大。
2. 另一方面,不要修改你解决方案中任何一个应用程序的 .csproj 文件。 原因:可执行文件需要在其各自的子目录中拥有它们所需的所有私有构建库,但每个应用程序的构建应该负责直接从其各自的子目录中单独收集每个依赖项到应用程序的子目录中。
这种方法非常完美,因为类库的 .csproj 可能引用多个其他类库,但可执行文件的 .csproj 通常不会引用另一个可执行文件。因此,对于每个本地构建的库来说,在它的 bin 文件夹中唯一的 .dll 就是它自己,而每个本地构建的应用程序将包含它所引用的所有本地构建的库。
方便的是,对于那些不是由您的解决方案构建的引用库,什么也不会改变,因为这些库通常使用 而不是 ,而我们根本没有修改前者标签。但请注意刚才提到的假设;如果您的某些项目违反了这个假设,您可能需要进行一些调整。
[1.] 可靠性改进可能与文件冲突有关,这些冲突可能在依赖图中从多个不同路径收集相同库时发生,特别是在并发构建中。

请纠正我,但我认为这不正确。通过使用私有属性标记<ProjectReference>,您表明您不希望将引用的项目DLL复制到生成文件夹中。这不包括您在该类库中可能使用的其他软件包引用。此外,如果您最终在<Reference>节点中包含false属性,则未在应用程序项目中明确引用的传递依赖项将不会被复制到生成文件夹中。 - Pavisa
@Pavisa 确实,传递性引用必须由开发人员在没有IDE辅助的情况下维护——但仅适用于可执行的csprojs,而且至关重要的是不适用于DLL。我之所以这么说是因为(至少对我来说)每个*.exe*的库比例通常是压倒性的,因此考虑构建速度的权衡时,这并不会带来不便。此外,这种维护“负担”非常微不足道,因为在更改库引用后,当您运行.exe时,您将收到有关缺少库的完整详细信息,并且仅会出现一次。根据我的经验,这完全不是问题。 - Glenn Slayden
维护这些依赖项的建议方式是什么?您是否建议将传递引用明确添加到.csproj文件中,让构建工具处理它,还是在构建过程后手动添加它们?我想在CI/CD管道中找到最简单的方法来做到这一点。我的问题是未使用的传递依赖项使我的构建变得不合理臃肿。 - Pavisa
@Pavisa 依赖项在 .csproj 文件中像正常情况下一样维护,可以根据需要混合使用 Visual Studio 项目引用,并且所有 IDE 编辑/构建/测试/运行功能都能够正常工作。这里唯一的区别是:1.) 仅在类库 .csproj 文件中 使用 <private>,2.) 您指出的维护问题很少(并且不重要),以及3.) 作为交换,每次构建时不会复制 1,000 个支持 .dll 文件到从未“执行”的位置,后者事实上使它们的存在变得无用甚至有害。 - Glenn Slayden
@Pavisa 总之,以防我表述不清,您的观点可能适用于某些构建方案,因此我的假设是:1.) 与所有解决方案相比,通常很少有应用程序(apps),而有许多动态链接库(dlls)。 2.) 解决方案应用程序引用的大多数或所有dll实际上都是由这些可执行文件(exes)明确引用的(使您的问题无关紧要);3.) .csproj交叉引用的工作流不会一直改变,或者很少出现在调试器运行时明显暴露的错误,可以轻松检测和解决。 - Glenn Slayden

4

使用此NuGet包,没有任何保证。在安装脚本中有一些预定义的过滤,会跳过某些引用。此外,它仅在NuGet包安装时运行。NuGet包可能已经安装,并且在此期间添加了新的引用。而且,如果在其他包恢复之前还原NuGet包,那么其他包可能会重置其引用程序集的本地复制属性。不推荐这种方法。最好使用目标来覆盖复制本地属性并进行自定义过滤。 - vezenkov

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