34

I was looking for the correct way to ensure appsettings.json is copied to the bin directory when building my project.

This article recommends using "none update"

<ItemGroup>
    <None Update="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

And this stackoverflow answer recommends using "none include":

<ItemGroup>
    <None Include="appsettings.json" CopyToOutputDirectory="Always" />
</ItemGroup>

What is the difference between update and include?

user453441
  • 1,026
  • 2
  • 13
  • 26

2 Answers2

13

What is the difference between update and include?

According to this document:

Usage:

Include attribute => The file or wildcard to include in the list of items.

Update attribute => Enables you to modify metadata of a file that was included by using a glob.

Working Scope:

Include attribute:

1.Works for both normal .net framewrok(non-sdk format), .net core and .net standard(sdk format), also C++ projects...

2.Used in many VS versions(As I know, from VS2010 to VS2019)

3.Include works even in ItemGroup in Targets

Update attribute:

1.Works for .net core projects

2.Available in VS2017 and later

3.Not supported when using it in an ItemGroup inside a Target

Tests and results:

First Test when c.txt is not included:

  <ItemGroup>
    <DoSth Include="a.txt"/>
    <DoSth Include="b.txt"/>
    <DoSth Update="c.txt" Metadata="test"/>
  </ItemGroup>

  <Target Name="MyTest" AfterTargets="build">
    <Message Text="@(DoSth)" Importance="high"/>
  </Target>
  <!--The output is a.txt and b.txt, no c.txt.-->

Second Test when c.txt is included:

  <ItemGroup>
    <DoSth Include="a.txt"/>
    <DoSth Include="b.txt"/>
    <DoSth Include="c.txt" Metadata="original"/>
    <DoSth Update="c.txt" Metadata="test"/>
  </ItemGroup>

  <Target Name="MyTest" AfterTargets="build">
    <Message Text="@(DoSth)" Importance="high"/> <!--The output files are a.txt,b.txt and c.txt.-->
    <Message Text="%(DoSth.Identity)+%(DoSth.Metadata)" Importance="high"/>  <!--The output Metadata of c.txt is test instead of original-->
  </Target>

So it's obvious that Update attribute is only used to update one Item's metadata. It has no function to include some files. And it will only work when one file has been Included in one Item.

To clarify something when using Update + appsettings.json in .net core console and asp.net core web app:

In Asp.net core web application:

If you create a new asp.net core web project, you can see the appsettings.json file in Solution Explorer by default.But if you read the xx.csproj you can't find the definition there, the definitions is not explicitly shown in xx.csproj for sdk-format.

In this situation, if we use script like <None Include="appsettings.json" CopyToOutputDirectory="Always" />, it always works to copy that file to output.

But for script <None Update="appsettings.json" CopyToOutputDirectory="PreserveNewest" />, it works only when there's no statement like <Content xxx="appsettings.json"...> in the project file.

And I recommend you do this by VS UI instead manual editing xx.csproj. We can change the Build Action and Copy To Output Settings in Property Window:

enter image description here

If we set the build action to be None, and Copy to Output to be Copy if Never of Copy Always, in xx.csproj we can find:

  <ItemGroup>
    <Content Remove="appsettings.json" />
  </ItemGroup>

  <ItemGroup>
    <None Include="appsettings.json">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </None>
  </ItemGroup>

So VS use None+Include behind for this. According to all above, I suggest you use None+Include instead of None+Update in your project. Also, the most recommended way is to control the copy behavior by UI in VS IDE instead of manual editing. Hope all above helps :)

Update1:

We can add this script in project file to output the info about None Items and Content Items that not shown in csproj:

  <Target Name="TrytoFindDefinitionsNowShown" AfterTargets="build">
    <Message Text="None: @(None)" Importance="high"/>
    <Message Text="Content: @(Content)" Importance="high"/>
  </Target>

Update2:

I just did some tests and find even in .net core console project in VS, though no definitions in csproj, the update can work. Strange?

And finally I found this is because that definition is hidden or not shown in csproj file, actually when you have a appsettings.json file in project, you've already have a <None Include="appsettings.json"... defined(sometimes Content+include, ikt depends on the way you add that file), though that's not valid to see in csproj, see my screenshot:

enter image description here

So that's why Update can work for the copy behavior though it's something used to modify metadata. Before we use the Update, there already exists the definition None+Include+Appsettings.json though that's not shown in csproj. (For sdk format)

Update3:

I did some little changes to script in Update1 to display the <None Include="appsettings.json">'s metadata.

enter image description here

So in a .net core console project, appsettings.json file by default is

<None Include="appsettings.json" CopyToOutputDirectory="Never" /> 

That's why both None+Include+Always(Overwrite) and None+Update+Always or PreserveNewest(Modify the metadata) work for copy behavior. And it can describe why by default that file won't be copied even VS have hidden None+Include definitions somewhere. Because the CopyToOutputDirectory is set to be Never by default.

LoLance
  • 25,666
  • 1
  • 39
  • 73
  • 1
    You said that `update Enables you to modify metadata of a file that was included by using a glob.` When I used the update snippet, I confirmed that the appsettings.json was being copied over to the output folder when it wasn't there before. I also confirmed that if the contents change in the source appsettings.json, the copy in the bin directory is updated after a build. So it seems it isn't just for updating metadata? – user453441 Oct 30 '19 at 05:15
  • @user453441 For `when it wasn't there before`? Do you test it in asp.net core project? Please see my statement about `In Asp.net core web application`. Those definitions are hidden and not explicitly shown in xx.csproj for sdk-format. Compare the content of project file from an empty .net framework console and an empty .net core console, you can see the difference. The new SDK-Format is a much more clear way. – LoLance Oct 30 '19 at 05:20
  • No I am using a .net core console application (target framework netcoreapp3.0), and I am using VS code. – user453441 Oct 30 '19 at 05:23
  • @user453441 I guess the Update2 can help to resolve your puzzle and confirm the usage of Update. Though the definition is not shown in csproj, we can use target+message task to output the hidden definitions. Hope it helps :) – LoLance Oct 30 '19 at 05:45
  • 1
    I'm sorry but I'm having a hard time reading your answer, or even just the Update2 section by itself. If I already have a `none include` or a `content include` that is "hidden or not shown", doesn't that mean the appsettings.json will be copied over to the output folder even if I don't add my `none update`?? I upvoted your answer because I appreciate your help researching the problem. – user453441 Oct 31 '19 at 01:44
  • @user453441 Hmm, my fault :( Actually I should add more details to describe `If I already have a none include or a content include that is "hidden or not shown", doesn't that mean the appsettings.json will be copied over to the output folder even if I don't add my none update??` . I will update the answer sooner to clarify it and help if someone has similar puzzle :) – LoLance Oct 31 '19 at 01:49
  • Perhaps Update3 can resolve your puzzle in last comment... You can try the script in Update3(do a little change to script in Update1) to output the hidden definitions of your appsettings.json file. May it helps :-( – LoLance Oct 31 '19 at 02:04
  • While I can appreciate the effort you put in this answer, I still don't quite understand the difference. What does it mean to "modify metadata"? What metadata? – Rob L Dec 07 '21 at 10:57
0

Summary: In SDK-style projects, there's a good chance the files you would include are already included in the project by the default rules. If so, <None Include=...> is a bit redundant (albeit harmless), so you may choose to prefer <None Update=...>.


As documented in this table, the update attribute is available "only for .NET Core projects in Visual Studio 2017 or later". This means it is used in SDK-style projects.

In SDK-style projects, some files are included by default as Compile, EmbeddedResource, or None items. Basically, files in your project folder and its subfolders are Compile items if they have a language extension like .cs, are EmbeddedResource items if they are .resx, and are None items for almost all other cases (excluding a few things like .*proj and .sln files).

Since these files are already included in the project, there's no need to explicitly include them with <None Include=...>. If you want to add some additional metadata like CopyToOutputDirectory="PreserveNewest", it suffices to use <None Update=...>

That said, <None Include=...> will still also work perfectly well, it's just a bit redundant because it's including a file which was already included. If pressed to give a recommendation, I'd say use Update except where Include is actually needed, but generally it shouldn't matter much.

Note however that if you set either the EnableDefaultItems or EnableDefaultNoneItems build properties to false then you will actually need to use <None Include=...> to add your items. (Both properties are true by default.)

Tim Goodman
  • 23,308
  • 7
  • 64
  • 83