为何MSBuild会忽略Directory.Build.props中设置的CopyToOutputDirectory?
核心问题
你遇到的问题是Directory.Build.props的导入时机导致Update指令失效:
MSBuild会先导入Directory.Build.props,再执行项目文件里的默认文件包含逻辑(自动把项目目录下的appsettings.json标记为None项)。当你在props里写<None Update="appsettings.json">时,此时项目中还不存在这个None项,Update找不到目标,自然不会生效。而直接写在.csproj里时,是在默认文件包含之后执行,所以能匹配到已存在的项。
解决方案
方案1:改用Directory.Build.targets
把配置从Directory.Build.props移到仓库根目录的Directory.Build.targets文件(没有就新建),内容如下:
<Project> <ItemGroup> <None Update="appsettings.json"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> </ItemGroup> </Project>
Directory.Build.targets是在项目文件之后导入的,此时项目已经自动包含了appsettings.json作为None项,Update指令能正确匹配并修改属性。
方案2:在Directory.Build.props中延迟处理
如果必须用props文件,通过Target延迟到文件项生成后再更新:
<Project> <Target Name="UpdateAppSettingsCopyBehavior" BeforeTargets="BeforeBuild"> <ItemGroup> <None Update="appsettings.json"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> </ItemGroup> </Target> </Project>
这个Target会在BeforeBuild阶段执行,此时默认的文件包含逻辑已经完成,Update能找到对应的None项。
验证步骤
运行dotnet msbuild -v:diag,查看输出中appsettings.json的属性,应该会显示:
None .editorconfig appsettings.json CopyToOutputDirectory = PreserveNewest
和直接写在.csproj里的效果一致。
另外注意你之前的诊断Target有语法错误:Condition=Exists('appsettings.json')"少了开头的引号,修正为Condition="Exists('appsettings.json')"后,诊断消息会更准确。
内容的提问来源于stack exchange,提问作者Ian Kemp - SO dead by AI greed

