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
?
None - The file is not included in the project output group and is not compiled in the build process. An example is a text file that contains documentation, such as a Readme file. Content - The file is not compiled, but is included in the Content output group.
What is a CSProj file? Files with CSPROJ extension represent a C# project file that contains the list of files included in a project along with the references to system assemblies.
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 andasp.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
:
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:
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With