If you been given the unenviable task of migrating your .NET Core project from ‘project.json’to MSBuild (csproj), you are likely to find your muscle memory disrupted and thedocumentation lacking. Automated upgrades in Visual Studio and .NET Core CLI mayauto-generate a csproj file for you, but they won’t tell you how to do things youalready know how to do in project.json. Here is the most exhaustive list I can createof all the project.json knobs as they exist in Microsoft.NET.Sdk.
Update: this has been made part of official Microsoft documentation.project.json and csproj comparison
See also:Part 2 - Caveats of project.json to MSBuild conversion
See also:Old csproj to new csproj: Visual Studio 2017 upgrade guide
Missing something? Post your question in comment section below and I will update this post.
March update:
.NET Core CLI 1.0.0 Downloads. It shipped!
February update:
(As of February 2017) the latest .NET Core CLI is the RC4 release.This is required to use the MSBuild-based .NET Core SDK.The RC4 version of dotnet includes a new command,dotnet migrate
. This command will attemptto automatically convert allproject.json projects to MSBuild.
For help on usage, executedotnet migrate --help
.
Tip: if you get an error, “No executable found matching command “dotnet-migrate””, check that:
dotnet --info
to see which version you are using.March update:
Visual Studio 2017 Downloads. It shipped, too!
Original post
Visual Studio 2017 RC Downloads
The same auto-upgrade feature of dotnet-migrate is available in Visual Studio 2017.When attempting to open a Visual Studiosolution that has “xproj” projects in it, VS will convert from project.json to the new MSBuild format.(xproj was a VS wrapper for project.json).
The new format, *.csproj, is an XML based format. Below are examples of how toconfigure a .NET Core, .NET Standard, or .NET Framework project using the new Microsoft.NET.Sdk.
For all examples below, assume the XML is inside this root node:
<ProjectSdk="Microsoft.NET.Sdk"ToolsVersion="15.0"></Project>
{"name":"MyProjectName"}
Not supported. In csproj, this is determined by the filename, e.g.MyProjectName.csproj
.
{"version":"1.0.0-alpha-*"}
<PropertyGroup><VersionPrefix>1.0.0</VersionPrefix><VersionSuffix>alpha</VersionSuffix></PropertyGroup>
You can also useVersion
, but this may override version settings during packaging.
<PropertyGroup><Version>1.0.0-alpha</Version></PropertyGroup>
{"authors":["Anne","Bob"],"company":"Contoso","language":"en-US","title":"My library","description":"This is my library.\r\nAnd it's really great!","copyright":"Nugetizer 3000","userSecretsId":"xyz123"}
<PropertyGroup><Authors>Anne;Bob<Authors><Company>Contoso<Company><NeutralLanguage>en-US</NeutralLanguage><AssemblyTitle>My library</AssemblyTitle><Description>This is my library.And it's really great!</Description><Copyright>Nugetizer 3000</Copyright><UserSecretsId>xyz123</UserSecretsId></PropertyGroup>
{"frameworks":{"netcoreapp1.0":{}}}
<PropertyGroup><TargetFramework>netcoreapp1.0</TargetFramework></PropertyGroup>
{"frameworks":{"netcoreapp1.0":{},"net451":{}}}
<PropertyGroup><TargetFrameworks>netcoreapp1.0;net451</TargetFrameworks></PropertyGroup>
NB: If the dependency is aproject, not a package the format is different. Seethe section below ondependency types
{"dependencies":{"Microsoft.AspNetCore":"1.1.0"}}
<ItemGroup><PackageReferenceInclude="Microsoft.AspNetCore"Version="1.1.0"/></ItemGroup>
{"framework":{"net451":{"dependencies":{"System.Collections.Immutable":"1.3.1"}},"netstandard1.5":{"dependencies":{"Newtonsoft.Json":"9.0.1"}}}}
<ItemGroupCondition="'$(TargetFramework)'=='net451'"><PackageReferenceInclude="System.Collections.Immutable"Version="1.3.1"/></ItemGroup><ItemGroupCondition="'$(TargetFramework)'=='netstandard1.5'"><PackageReferenceInclude="Newtonsoft.Json"Version="9.0.1"/></ItemGroup>
{"dependencies":{"YamlDotNet":"4.0.1-pre309"},"frameworks":{"netcoreapp1.0":{"imports":["dnxcore50","dotnet"]}}}
<PropertyGroup><PackageTargetFallback>dnxcore50;dotnet</PackageTargetFallback></PropertyGroup><ItemGroup><PackageReferenceInclude="YamlDotNet"Version="4.0.1-pre309"/></ItemGroup>
{"dependencies":{"MyOtherProject":"1.0.0-*","AnotherProject":{"type":"project"}}}
<ItemGroup><ProjectReferenceInclude="..\MyOtherProject\MyOtherProject.csproj"/><ProjectReferenceInclude="..\AnotherProject\AnotherProject.csproj"/></ItemGroup>
NB: this will break the way thatdotnet pack --version-suffix $suffix
determines thedependency version of a project reference. Seehttps://github.com/NuGet/Home/issues/4337
{"dependencies":{"Microsoft.EntityFrameworkCore.Design":{"version":"1.1.0","type":"build"}}}
<ItemGroup><PackageReferenceInclude="Microsoft.EntityFrameworkCore.Design"Version="1.1.0"PrivateAssets="All"/></ItemGroup>
{"dependencies":{"Microsoft.NETCore.App":{"version":"1.1.0","type":"platform"}}}
There is no equivalent in csproj. Microsoft.NET.Sdk automagically knows about Microsoft.NETCore.App.
{"runtimes":{"win7-x64":{},"osx.10.11-x64":{},"ubuntu.16.04-x64":{}}}
<PropertyGroup><RuntimeIdentifiers>win7-x64;osx.10-11-x64;ubuntu.16.04-x64</RuntimeIdentifiers></PropertyGroup>
In project.json, defining a ‘runtimes’ section means the app was standalone duringbuild and publish.In MSBuild, all projects are ‘portable’ during build, but can be published asstandalone.
dotnet publish --framework netcoreapp1.0 --runtime osx.10.11-x64
{"tools":{"Microsoft.EntityFrameworkCore.Tools.DotNet":"1.0.0-*"}}
<ItemGroup><DotNetCliToolReferenceInclude="Microsoft.EntityFrameworkCore.Tools.DotNet"Version="1.0.0"/></ItemGroup>
NB: “imports” on tools are no longer supported in csproj. Tools that need imports will not work withthe new Microsoft.NET.Sdk.
See alsoFiles.
{"buildOptions":{"emitEntryPoint":true}}
<PropertyGroup><OutputType>Exe</OutputType></PropertyGroup>
{"buildOptions":{"emitEntryPoint":false}}
<PropertyGroup><OutputType>Library</OutputType><!-- or, omit altogether. It defaults to 'Library' --></PropertyGroup>
This one gets special mention because it expands to three properties in MSBuild.
{"buildOptions":{"keyFile":"MyKey.snk"}}
<PropertyGroup><AssemblyOriginatorKeyFile>MyKey.snk</AssemblyOriginatorKeyFile><SignAssembly>true</SignAssembly><PublicSignCondition="'$(OS)' != 'Windows_NT'">true</PublicSign></PropertyGroup>
{"buildOptions":{"warningsAsErrors":true,"nowarn":["CS0168","CS0219"],"xmlDoc":true,"preserveCompilationContext":true,"outputName":"Different.AssemblyName","debugType":"portable","allowUnsafe":true,"define":["TEST","OTHERCONDITION"]}}
<PropertyGroup><TreatWarningsAsErrors>true</TreatWarningsAsErrors><NoWarn>$(NoWarn);CS0168;CS0219</NoWarn><GenerateDocumentationFile>true</GenerateDocumentationFile><PreserveCompliationContext>true</PreserveCompliationContext><AssemblyName>Different.AssemblyName</AssemblyName><DebugType>portable</DebugType><AllowUnsafeBlocks>true</AllowUnsafeBlocks><DefineConstants>$(DefineConstants);TEST;OTHERCONDITION</DefineConstants></PropertyGroup>
See alsoFiles.
{"packOptions":{"summary":"A bundle of cats","tags":["hyperscale","cats"],"owners":["Nate","Jenna"],"releaseNotes":"Version 1.0","iconUrl":"https://icons.com/awesomeness.png","projectUrl":"https://github.com/natemcmaster","licenseUrl":"https://www.apache.org/licenses/LICENSE-2.0","requireLicenseAcceptance":false,"repository":{"type":"git","url":"https://github.com/natemcmaster/natemcmaster.github.io"}}}
<PropertyGroup><Description>A bundle of cats</Description><PackageTags>hyperscale;cats</PackageTags><PackageReleaseNotes>Version 1.0</PackageReleaseNotes><PackageIconUrl>https://icons.com/awesomeness.png</PackageIconUrl><PackageProjectUrl>https://github.com/natemcmaster</PackageProjectUrl><PackageLicenseUrl>https://www.apache.org/licenses/LICENSE-2.0</PackageLicenseUrl><PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance><RepositoryType>git</RepositoryType><RepositoryUrl>https://github.com/natemcmaster/natemcmaster.github.io</RepositoryUrl><!-- regrettably, 'owners' does not translate to MSBuild. --></PropertyGroup>
{"scripts":{"precompile":"generateCode.cmd","postpublish":["obfuscate.cmd","removeTempFiles.cmd"]}}
Their equivalent in MSBuild are targets.
<TargetName="MyPreCompileTarget"BeforeTargets="Build"><ExecCommand="generateCode.cmd"/></Target><TargetName="MyPostCompileTarget"AfterTargets="Publish"><ExecCommand="obfuscate.cmd"/><ExecCommand="removeTempFiles.cmd"/></Target>
{"runtimeOptions":{"configProperties":{"System.GC.Server":true,"System.GC.Concurrent":true,"System.GC.RetainVM":true,"System.Threading.ThreadPool.MinThreads":10,"System.Threading.ThreadPool.MaxThreads":100}}}
<PropertyGroup><ServerGarbageCollection>true</ServerGarbageCollection><ConcurrentGarbageCollection>true</ConcurrentGarbageCollection><RetainVMGarbageCollection>true</RetainVMGarbageCollection><!-- I'm not suggesting these settings...just showing usage ;) --><ThreadPoolMinThreads>10</ThreadPoolMinThreads><ThreadPoolMaxThreads>100</ThreadPoolMaxThreads></ProeprtyGroup>
Tip: If you are working on a web project, specifying<Project Sdk="Microsoft.NET.Sdk.Web">
will automatically default ServerGarbageCollection to true.
SeeCoreCLR configurationfor information on these settings.
{"shared":"shared/**/*.cs"}
Not supported in csproj. You must instead create a ‘contentFiles’ package. Seehttps://docs.nuget.org for more info.
In project.json, build and pack could be extended to compile and embed from different folders.In MSBuild, this is done using items. Here is a common conversion:
{"buildOptions":{"compile":{"copyToOutput":"notes.txt","include":"../Shared/*.cs","exclude":"../Shared/Not/*.cs"},"embed":{"include":"../Shared/*.resx"}},"packOptions":{"include":"Views/","mappings":{"some/path/in/project.txt":"in/package.txt"}},"publishOptions":{"include":["files/","publishnotes.txt"]}}
<ItemGroup><CompileInclude="..\Shared\*.cs"Exclude="..\Shared\Not\*.cs"/><EmbeddedResourceInclude="..\Shared\*.resx"/><ContentInclude="Views\**\*"PackagePath="%(Identity)"/><NoneInclude="some/path/in/project.txt"Pack="true"PackagePath="in/package.txt"/><NoneInclude="notes.txt"CopyToOutputDirectory="Always"/><!-- CopyToOutputDirectory = { Always, PreserveNewest, Never } --><ContentInclude="files\**\*"CopyToPublishDirectory="PreserveNewest"/><NoneInclude="publishnotes.txt"CopyToPublishDirectory="Always"/><!-- CopyToPublishDirectory = { Always, PreserveNewest, Never } --><!-- you can set both copy output and publish directories--><NoneInclude="testasset.txt"CopyToOutputDirectory="Always"CopyToPublishDirectory="Always"/><!-- alternatively, use nested XML attributes. They're functionally the same--><NoneInclude="testasset2.txt"><CopyToOutputDirectory>Always</CopyToOutputDirectory><CopyToPublishDirectory>Always</CopyToPublishDirectory></None></ItemGroup>
NB: many of default globbing patterns are added automatically by Microsoft.NET.Sdkand Microsoft.NET.Sdk.Web.
All MSBuild ItemGroup’s support ‘Include’, ‘Exclude’, ‘Update’, and ‘Remove’.
Package layout inside the nupkg can be modified withPackagePath="path"
.
Except for “Content”, most item groups require explicitly addingPack="true"
tobe included in the package. By default, this will be put in the ‘content’ folderin a package.PackagePath="%(Identity)"
is a short way of setting package pathto the project-relative file path.
{"testRunner":"xunit","dependencies":{"dotnet-test-xunit":"<any>"}}
<ItemGroup><PackageReferenceInclude="Microsoft.NET.Test.Sdk"Version="15.0.0"/><PackageReferenceInclude="xunit"Version="2.2.0"/><PackageReferenceInclude="xunit.runner.visualstudio"Version="2.2.0"/></ItemGroup>
{"testRunner":"mstest","dependencies":{"dotnet-test-mstest":"<any>"}}
<ItemGroup><PackageReferenceInclude="Microsoft.NET.Test.Sdk"Version="15.0.0"/><PackageReferenceInclude="MSTest.TestAdapter"Version="1.1.12"/><PackageReferenceInclude="MSTest.TestFramework"Version="1.1.11"/></ItemGroup>
For more details, checkoutPart 2 - Caveats of project.json to MSBuild conversion