A mapping between project.json and csproj properties
During the development of the .NET Core tooling, an important design change was made to no longer support project.json files and instead move the .NET Core projects to the MSBuild/csproj format.
This article shows how the settings in project.json are represented in the MSBuild/csproj format so you can learn how to use the new format and understand the changes made by the migration tools when you're upgrading your project to the latest version of the tooling.
The csproj format
The new format, *.csproj, is an XML-based format. The following example shows the root node of a .NET Core project using the Microsoft.NET.Sdk
. For web projects, the SDK used is Microsoft.NET.Sdk.Web
.
<Project Sdk="Microsoft.NET.Sdk">
...
</Project>
Common top-level properties
name
{
"name": "MyProjectName"
}
No longer supported. In csproj, this is determined by the project filename, which usually matches the directory name. For example, MyProjectName.csproj
.
By default, the project filename also specifies the value of the <AssemblyName>
and <PackageId>
properties.
<PropertyGroup>
<AssemblyName>MyProjectName</AssemblyName>
<PackageId>MyProjectName</PackageId>
</PropertyGroup>
The <AssemblyName>
will have a different value than <PackageId>
if buildOptions\outputName
property was defined in project.json.
For more information, see Other common build options.
version
{
"version": "1.0.0-alpha-*"
}
Use the VersionPrefix
and VersionSuffix
properties:
<PropertyGroup>
<VersionPrefix>1.0.0</VersionPrefix>
<VersionSuffix>alpha</VersionSuffix>
</PropertyGroup>
You can also use the Version
property, but this may override version settings during packaging:
<PropertyGroup>
<Version>1.0.0-alpha</Version>
</PropertyGroup>
Other common root-level options
{
"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
One target framework
{
"frameworks": {
"netcoreapp1.0": {}
}
}
<PropertyGroup>
<TargetFramework>netcoreapp1.0</TargetFramework>
</PropertyGroup>
Multiple target frameworks
{
"frameworks": {
"netcoreapp1.0": {},
"net451": {}
}
}
Use the TargetFrameworks
property to define your list of target frameworks. Use semi-colon to separate multiple framework values.
<PropertyGroup>
<TargetFrameworks>netcoreapp1.0;net451</TargetFrameworks>
</PropertyGroup>
dependencies
Important
If the dependency is a project and not a package, the format is different. For more information, see the dependency type section.
NETStandard.Library metapackage
{
"dependencies": {
"NETStandard.Library": "1.6.0"
}
}
<PropertyGroup>
<NetStandardImplicitPackageVersion>1.6.0</NetStandardImplicitPackageVersion>
</PropertyGroup>
Microsoft.NETCore.App metapackage
{
"dependencies": {
"Microsoft.NETCore.App": "1.0.0"
}
}
<PropertyGroup>
<RuntimeFrameworkVersion>1.0.3</RuntimeFrameworkVersion>
</PropertyGroup>
The <RuntimeFrameworkVersion>
value in the migrated project is determined by the version of SDK that's installed.
Top-level dependencies
{
"dependencies": {
"Microsoft.AspNetCore": "1.1.0"
}
}
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.0" />
</ItemGroup>
Per-framework dependencies
{
"framework": {
"net451": {
"dependencies": {
"System.Collections.Immutable": "1.3.1"
}
},
"netstandard1.5": {
"dependencies": {
"Newtonsoft.Json": "9.0.1"
}
}
}
}
<ItemGroup Condition="'$(TargetFramework)'=='net451'">
<PackageReference Include="System.Collections.Immutable" Version="1.3.1" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='netstandard1.5'">
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
</ItemGroup>
imports
{
"dependencies": {
"YamlDotNet": "4.0.1-pre309"
},
"frameworks": {
"netcoreapp1.0": {
"imports": [
"dnxcore50",
"dotnet"
]
}
}
}
<PropertyGroup>
<PackageTargetFallback>dnxcore50;dotnet</PackageTargetFallback>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="YamlDotNet" Version="4.0.1-pre309" />
</ItemGroup>
Note
The PackageTargetFallback
property is deprecated. Use AssetTargetFallback instead.
dependency type
type: project
{
"dependencies": {
"MyOtherProject": "1.0.0-*",
"AnotherProject": {
"type": "project"
}
}
}
<ItemGroup>
<ProjectReference Include="..\MyOtherProject\MyOtherProject.csproj" />
<ProjectReference Include="..\AnotherProject\AnotherProject.csproj" />
</ItemGroup>
Note
This will break the way that dotnet pack --version-suffix $suffix
determines the dependency version of a project reference.
type: build
{
"dependencies": {
"Microsoft.EntityFrameworkCore.Design": {
"version": "1.1.0",
"type": "build"
}
}
}
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.0" PrivateAssets="All" />
</ItemGroup>
type: platform
{
"dependencies": {
"Microsoft.NETCore.App": {
"version": "1.1.0",
"type": "platform"
}
}
}
There is no equivalent in csproj.
runtimes
{
"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>
Standalone apps (self-contained deployment)
In project.json, defining a runtimes
section means the app was standalone during
build and publish.
In MSBuild, all projects are portable during build, but can be published as
standalone.
dotnet publish --framework netcoreapp1.0 --runtime osx.10.11-x64
For more information, see Self-contained deployments (SCD).
tools
{
"tools": {
"Microsoft.EntityFrameworkCore.Tools.DotNet": "1.0.0-*"
}
}
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" />
</ItemGroup>
Note
imports
on tools are not supported in csproj. Tools that need imports will not work withMicrosoft.NET.Sdk
.DotNetCliToolReference
is deprecated in favor of local tools.
buildOptions
See also Files.
emitEntryPoint
{
"buildOptions": {
"emitEntryPoint": true
}
}
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
If emitEntryPoint
was false
, the value of OutputType
is converted to Library
, which is the default value:
{
"buildOptions": {
"emitEntryPoint": false
}
}
<PropertyGroup>
<OutputType>Library</OutputType>
<!-- or, omit altogether. It defaults to 'Library' -->
</PropertyGroup>
keyFile
{
"buildOptions": {
"keyFile": "MyKey.snk"
}
}
The keyFile
element expands to three properties in MSBuild:
<PropertyGroup>
<AssemblyOriginatorKeyFile>MyKey.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly>
<PublicSign Condition="'$(OS)' != 'Windows_NT'">true</PublicSign>
</PropertyGroup>
Other common build options
{
"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>
<PreserveCompilationContext>true</PreserveCompilationContext>
<AssemblyName>Different.AssemblyName</AssemblyName>
<DebugType>portable</DebugType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>$(DefineConstants);TEST;OTHERCONDITION</DefineConstants>
</PropertyGroup>
packOptions
See also Files.
Common pack options
{
"packOptions": {
"summary": "numl is a machine learning library intended to ease the use of using standard modeling techniques for both prediction and clustering.",
"tags": ["machine learning", "framework"],
"releaseNotes": "Version 0.9.12-beta",
"iconUrl": "http://numl.net/images/ico.png",
"projectUrl": "http://numl.net",
"licenseUrl": "https://raw.githubusercontent.com/sethjuarez/numl/master/LICENSE.md",
"requireLicenseAcceptance": false,
"repository": {
"type": "git",
"url": "https://raw.githubusercontent.com/sethjuarez/numl"
},
"owners": ["Seth Juarez"]
}
}
<PropertyGroup>
<!-- summary is not migrated from project.json, but you can use the <Description> property for that if needed. -->
<PackageTags>machine learning;framework</PackageTags>
<PackageReleaseNotes>Version 0.9.12-beta</PackageReleaseNotes>
<PackageIcon>ico.png</PackageIcon>
<PackageProjectUrl>http://numl.net</PackageProjectUrl>
<PackageLicenseUrl>https://raw.githubusercontent.com/sethjuarez/numl/master/LICENSE.md</PackageLicenseUrl>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://raw.githubusercontent.com/sethjuarez/numl</RepositoryUrl>
<!-- owners is not supported in MSBuild -->
</PropertyGroup>
There is no equivalent for the owners
element in MSBuild. For summary
, you can use the MSBuild <Description>
property. The value of summary
is not migrated automatically to that property, since that property is mapped to the description
element. PackageIconUrl is deprecated in favor of PackageIcon.
scripts
{
"scripts": {
"precompile": "generateCode.cmd",
"postpublish": [ "obfuscate.cmd", "removeTempFiles.cmd" ]
}
}
Their equivalents in MSBuild are targets:
<Target Name="MyPreCompileTarget" BeforeTargets="Build">
<Exec Command="generateCode.cmd" />
</Target>
<Target Name="MyPostCompileTarget" AfterTargets="Publish">
<Exec Command="obfuscate.cmd" />
<Exec Command="removeTempFiles.cmd" />
</Target>
runtimeOptions
{
"runtimeOptions": {
"configProperties": {
"System.GC.Server": true,
"System.GC.Concurrent": true,
"System.GC.RetainVM": true,
"System.Threading.ThreadPool.MinThreads": 4,
"System.Threading.ThreadPool.MaxThreads": 25
}
}
}
All settings in this group, except for the System.GC.Server
property, are placed into a file called
runtimeconfig.template.json in the project folder, with options lifted to the root object during the migration process:
{
"configProperties": {
"System.GC.Concurrent": true,
"System.GC.RetainVM": true,
"System.Threading.ThreadPool.MinThreads": 4,
"System.Threading.ThreadPool.MaxThreads": 25
}
}
The System.GC.Server
property is migrated into the csproj file:
<PropertyGroup>
<ServerGarbageCollection>true</ServerGarbageCollection>
</PropertyGroup>
However, you can set all those values in the csproj as well as MSBuild properties:
<PropertyGroup>
<ServerGarbageCollection>true</ServerGarbageCollection>
<ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
<RetainVMGarbageCollection>true</RetainVMGarbageCollection>
<ThreadPoolMinThreads>4</ThreadPoolMinThreads>
<ThreadPoolMaxThreads>25</ThreadPoolMaxThreads>
</PropertyGroup>
shared
{
"shared": "shared/**/*.cs"
}
Not supported in csproj. Instead, create include content files in your .nuspec file. For more information, see Including content files.
files
In project.json, build and pack could be extended to compile and embed from different folders. In MSBuild, this is done using items. The following example 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>
<Compile Include="..\Shared\*.cs" Exclude="..\Shared\Not\*.cs" />
<EmbeddedResource Include="..\Shared\*.resx" />
<Content Include="Views\**\*" PackagePath="%(Identity)" />
<None Include="some/path/in/project.txt" Pack="true" PackagePath="in/package.txt" />
<None Include="notes.txt" CopyToOutputDirectory="Always" />
<!-- CopyToOutputDirectory = { Always, PreserveNewest, Never } -->
<Content Include="files\**\*" CopyToPublishDirectory="PreserveNewest" />
<None Include="publishnotes.txt" CopyToPublishDirectory="Always" />
<!-- CopyToPublishDirectory = { Always, PreserveNewest, Never } -->
</ItemGroup>
Note
Many of the default globbing patterns are added automatically by the .NET Core SDK. For more information, see Default compilation includes.
All MSBuild ItemGroup
elements support Include
, Exclude
, and Remove
.
Package layout inside the .nupkg can be modified with PackagePath="path"
.
Except for Content
, most item groups require explicitly adding Pack="true"
to be included in the package. Content
will be put in the content folder in a package since the MSBuild <IncludeContentInPack>
property is set to true
by default.
For more information, see Including content in a package.
PackagePath="%(Identity)"
is a short way of setting package path to the project-relative file path.
testRunner
xUnit
{
"testRunner": "xunit",
"dependencies": {
"dotnet-test-xunit": "<any>"
}
}
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0-*" />
<PackageReference Include="xunit" Version="2.2.0-*" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0-*" />
</ItemGroup>
MSTest
{
"testRunner": "mstest",
"dependencies": {
"dotnet-test-mstest": "<any>"
}
}
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0-*" />
<PackageReference Include="MSTest.TestAdapter" Version="1.1.12-*" />
<PackageReference Include="MSTest.TestFramework" Version="1.1.11-*" />
</ItemGroup>