Migrate ASP.Net Core Application from XProj/Project.json to CSProj/MSBuild

In this tutorial, we are going to see how to migrate an existing ASP.Net Core Web Project which is based on XProj and Project.json to support new CSProj and MSBuild tooling. ASP.Net Core is a ground up design and to support multiple environments and platforms, ASP.Net team decided to go with XProj/Project.json approach which is light-weight and easy to modify dependencies, target frameworks etc. But the interoperability between different .Net Application Models (which are based on CSProj and MSBuild) with ASP.Net Core Applications faced challenges.

Dotnet team carefully evaluated all the options (moving all .Net Models to XProj/Project.json or building bridges between XProj and CSProj projects etc.,) and finally decide to move .Net Core projects to CSProj/MSBuild so that all .NET Application Models use the same tooling and build system.

First lets get started by creating a Project.json based ASP.Net Core project. I have used following Dotnet SDK and using VS Community 2015, we can create a new ASP.Net Core Project.


Now I have another machine, where I got VS 2017 RC (RC.4 build) installed (which includes latest MSBuild tools for Dotnet).


To migrate the project from XProj to CSProj (it is advised to keep a backup), we have to execute following dotnet migrate command.


Now lets compare project.json  and CSProj files.

  "dependencies": {
    "Microsoft.NETCore.App": {
      "version": "1.0.1",
      "type": "platform"
    "Microsoft.ApplicationInsights.AspNetCore": "1.0.0",
    "Microsoft.AspNetCore.Diagnostics": "1.0.0",
    "Microsoft.AspNetCore.Mvc": "1.0.1",
    "Microsoft.AspNetCore.Razor.Tools": {
      "version": "1.0.0-preview2-final",
      "type": "build"
    "Microsoft.AspNetCore.Routing": "1.0.1",
    "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
    "Microsoft.AspNetCore.Server.Kestrel": "1.0.1",
    "Microsoft.AspNetCore.StaticFiles": "1.0.0",
    "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
    "Microsoft.Extensions.Configuration.Json": "1.0.0",
    "Microsoft.Extensions.Logging": "1.0.0",
    "Microsoft.Extensions.Logging.Console": "1.0.0",
    "Microsoft.Extensions.Logging.Debug": "1.0.0",
    "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0",
    "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0"

  "tools": {
    "BundlerMinifier.Core": "2.0.238",
    "Microsoft.AspNetCore.Razor.Tools": "1.0.0-preview2-final",
    "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final"

  "frameworks": {
    "netcoreapp1.0": {
      "imports": [

  "buildOptions": {
    "emitEntryPoint": true,
    "preserveCompilationContext": true

  "runtimeOptions": {
    "configProperties": {
      "System.GC.Server": true

  "publishOptions": {
    "include": [

  "scripts": {
    "prepublish": [ "bower install", "dotnet bundle" ],
    "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]

And the converted CSProj file is shown below.

<Project Sdk="Microsoft.NET.Sdk.Web">


    <Content Update="wwwroot\**\*;**\*.cshtml;appsettings.json;web.config">

    <PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="1.0.0" />
    <PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="1.0.1" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.0.2" />
    <PackageReference Include="Microsoft.AspNetCore.Routing" Version="1.0.2" />
    <PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.0.1" />
    <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.0.2" />
    <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.0.1" />
    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.0.1" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.0.1" />
    <PackageReference Include="Microsoft.Extensions.Logging" Version="1.0.1" />
    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.0.1" />
    <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.0.1" />
    <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.0.1" />
    <PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink.Loader" Version="14.0.1" />

  <Target Name="PrepublishScript" BeforeTargets="PrepareForPublish">
    <Exec Command="bower install" />
    <Exec Command="dotnet bundle" />

    <DotNetCliToolReference Include="BundlerMinifier.Core" Version="2.2.301" />


Lets understand how the migration happened. First lets focus on frameworks section of project.json, it has been moved to TargetFramework and PackageTargetFallback nodes.

The Dependencies section has been migrate to ItemGroup as PackageReferences. One important change to look is that NETCore.App (the runtime) version is not specified as PackageReference, Dotnet SDK automatically knows the version. If we want to explicitly fix the dotnet SDK version then we can use global.json.

Tools section is migrated to ItemGroup with DotNetCliToolReference. In the process of conversion, not all tools are migrated as DotNetCliToolReferences. We can manually edit the CSProj file and add the missing tool references. Editing the CSProj file is very easy in VS 2017 RC, we can right click on the project in Visual Studio and select Edit CSProj option.

BuildOptions section has been migrated to OutputType (which cays whether the output was a DLL, EXE or Windows EXE) and PreserveCompilationContext nodes under PropertyGroup.

PublishOptions section has been moved to Content node under ItemGroup section. After Publish targets can be added as shown below.

<Target Name="PostpublishScript" AfterTargets="Publish">
  <Exec Command="customRun.cmd" />

RuntimeOptions section should be moved to a file with name runtimeconfig.template.json. This is a manual change (not sure why migration is not happened) which we have to do it by ourselves. In next releases of dotnet SDK, there is no need of new file, it would be part of CSProj itself.

  "configProperties": {
    "System.GC.Server": true

Now we can run our application using dotnet run and should be able to browse the ASP.Net Core site at localhost:5000 (default settings).

That’s it for now. Happy Coding and Stay Tuned!!!

You may also like...

  • James

    Good one!!!