Cover

Converting an ASP.NET Core RC1 Project to RC2

May 17, 2016
No Comments.

With the release of ASP.NET Core RC2, Microsoft hit a major milestone. But this change isn’t a trivial one. It’s a big change.

Since I’m updating my Pluralsight course on ASP.NET Core, I wanted to get a list of changes for the new version. I figured I’d share all the changes I could find converting a stock RC1 project to a RC2 one. It’s a big list, but hopefully manageable. Please share in comments and changes I missed so others can be helped!

Project.json

Frameworks:

First, change the framework to the new naming style. For dxcore use:

  "frameworks": {
    "netcoreapp1.0": {
      "imports": [
        "dotnet5.6",
        "dnxcore50",
        "portable-net45+win8"
      ]
    }
  },

For desktop .NET use:

  "frameworks": {
    "net452": { }
  },

compliationOptions

Change the compliationOptions to buildOptions:

  "buildOptions": {
    "emitEntryPoint": true
  },

Runtime

First, add the runtime as a dependency:

  "dependencies": {
    "Microsoft.NETCore.App": {
      "version": "1.0.0-rc2-3002702",
      "type": "platform"
    },

Naming Changes

Change any dependencies from RC1 to RC2:

"Microsoft.ApplicationInsights.AspNet": "1.0.0-rc2-final",

Change any Microsoft.AspNet.* dependencies to Microsoft.AspNetCore.*

  "dependencies": {
    ...
    "Microsoft.AspNetCore.Authentication.Cookies": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Diagnostics.Entity": "7.0.0-rc2-final",
    "Microsoft.AspNetCore.Identity.EntityFramework": "3.0.0-rc2-final",
    "Microsoft.AspNetCore.IISPlatformHandler": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Mvc": "6.0.0-rc2-final",
    "Microsoft.AspNetCore.Mvc.TagHelpers": "6.0.0-rc2-final",

Change assemblies with new names including (but probably not a complete list):

EntityFramework SqlServer –> Microsoft.EntityFrameworkCore.SqlServer

Microsoft.AspNet.Identity.EntityFramework -> Microsoft.AspNetCore.Identity.EntityFrameworkCore

Microsoft.AspNet.IISPlatformHandler -> Microsoft.AspNetCore.Server.IIS

Microsoft.AspNet.Diagnostics.Entity -> Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore

Microsoft.AspNet.Tooling.Razor –> Microsoft.AspNetCore.Razor.Tools

EntityFramework.Commands -> Microsoft.EntityFrameworkCore.Tools

Microsoft.Extensions.CodeGenerators.Mvc –> Microsoft.VisualStudio.Web.CodeGenerators.Mvc

Commands

The commands and tooling have changed a bit and in RC1 they require a little more work. You need to change their versions to an object tag (this is going to be a temporary change until the tooling gets past Preview):

    "Microsoft.AspNetCore.Razor.Tools": {
      "version": "1.0.0-preview1-final",
      "type": "build"
    },
    "Microsoft.EntityFrameworkCore.Tools": {
      "version": "1.0.0-preview1-final",
      "type": "build"
    },
    "Microsoft.VisualStudio.Web.CodeGenerators.Mvc": {
      "version": "1.0.0-preview1-final",
      "type": "build"
    }

You’ll also need to add a new tool called CodeGeneration Tools:

  "Microsoft.VisualStudio.Web.CodeGeneration.Tools": {
      "version": "1.0.0-preview1-final",
      "type": "build"
    },

Rename the ‘commands’ to ‘tools’. You’ll need to empty the collection and add the current tools from the preview like so:

  "tools": {
    "Microsoft.AspNetCore.Razor.Tools": {
      "version": "1.0.0-preview1-final",
      "imports": "portable-net45+win8+dnxcore50"
    },
    "Microsoft.AspNetCore.Server.IISIntegration.Tools": {
      "version": "1.0.0-preview1-final",
      "imports": "portable-net45+win8+dnxcore50"
    },
    "Microsoft.EntityFrameworkCore.Tools": {
      "version": "1.0.0-preview1-final",
      "imports": [
        "portable-net45+win8+dnxcore50",
        "portable-net45+win8"
      ]
    },
    "Microsoft.Extensions.SecretManager.Tools": {
      "version": "1.0.0-preview1-final",
      "imports": "portable-net45+win8+dnxcore50"
    },
    "Microsoft.VisualStudio.Web.CodeGeneration.Tools": {
      "version": "1.0.0-preview1-final",
      "imports": [
        "portable-net45+win8+dnxcore50",
        "portable-net45+win8"
      ]
    }
  },

Publishing

Remove the exclude and **publishExclude **sections entirely. You can replace them with the standard publishOptions section (or migrate the changes if you made any):

  "publishOptions": {
    "include": [
      "wwwroot",
      "Views",
      "appsettings.json",
      "web.config"
    ]
  },

Note that this is only including what has to be shipped with a published app instead of excluding what you don’t want. I think it’s simpler.

Checking the Changes

To see if all of this worked, open a console in the directory and type ‘dotnet restore’ to see if it finishes without any errors. You can simplify the output by putting “dotnet restore –v Minimal” to only see warnings and errors.

Changing the xproj File

The xproj file has reference to the older RC1 tools, so you’ll need to edit it and change it manually. Find and replace the following strings in it:

\DNX\Microsoft.DNX.Props –> \DotNet\Microsoft.DotNet.Props

\DNX\Microsoft.DNX.targets –> \DotNet.Web\Microsoft.DotNet.Web.targets

You’ll also want to change the BaseIntermediateOutputPath to:

<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>

Finally, add target framework just after the OutputPath:

    <OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
    <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>

You can close the file and reload the changes in Visual Studio.

Common Code Changes

Fix Startup.cs

Find the entry point (e.g. static void Main()). You have two options here, either you can change the code, or move it a separate Program.cs file. The new templates breaks it out into a separate file. It doesn’t actually matter, but for simplicity of my current projects, I’m just changing the Main and not creating a new file. Your choice. The new code should look like this:

    // Entry point for the application.
    public static void Main(string[] args)
    {
      var host = new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .UseStartup<Startup>()
        .Build();

      host.Run();
    }

You can see the buildup of the web server contains more details than before, but it also moves some of the ideas into the server configuration (e.g. UseIISIntegration). If you’re using the IISPlatformHandler middleware, you can remove it in the Configure method too.

Configuration Changes

In order to allow the runtime to find your configuration, you’ll have to set the base directory for configuration if you’re not already doing it:

    public Startup(IHostingEnvironment env)
    {
      // Set up configuration sources.

      var builder = new ConfigurationBuilder()
         .SetBasePath(env.ContentRootPath)
          .AddJsonFile("appsettings.json")
          .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

Note the new environment value of “ContentRootPath” which is where the configuration file is without having to copy it to wwwroot.

Also, the default appconfig.json file contains a bad logging value. The “Verbose” logging level no longer exists, instead, change it to Debug or one of the other values:

  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  }

You might want to see if anything else in your config has bad values as well, this is just the default config that is created with a new project.

Rename Namespaces

You’re going to want to replace the namespaces, in fact…I’d just do a search and replace from “using Microsoft.AspNet.” to “using Microsoft.AspNetCore.”. There will be other namespaces that need changed, but on the whole, adding **Core **will solve many problems. For example:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;

You’ll find that other systems need renaming with Core but the main ones (Entity Framework and Identity) are covered next.

Entity Framework Code

EntityFramework needs namespace changes (both in Startup.cs and in any DbContext derived classes):

Microsoft.Data.Entity –> Microsoft.EntityFrameworkCore

Add Microsoft.EntityFrameworkCore.Infrastructure too

In ConfigureServices, the configuration of your context objects has changed:

// RC1
services.AddEntityFramework()
    .AddSqlServer()
    .AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));

// RC2
services.AddDbContext<ApplicationDbContext>(options =>
  options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));

If you setup your connection string by overriding your OnConfiguring method of your DbContext class, you’re good to go, but if you want to do it in the startup.cs (like shown above), then you’ll need to change your context class(es) to pass in DbContextOptions to the base class:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
  public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
    : base(options)
  {
  }

Your DbContext class should now be configured.

Identity Changes

The code in Startup.cs hasn’t changed for Identity, but you’ll need to fix the namespaces in your IdentityUser (e.g. ApplicationUser unless you’ve renamed it) and IdentityContext classes. For example, the Identity namespace change:

using Microsoft.AspNetCore.Identity.EntityFrameworkCore;

namespace RC1toRC2.Models
{
  // Add profile data for application users by adding properties to the ApplicationUser class
  public class ApplicationUser : IdentityUser

Same idea for the IdentityContext class:

using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

namespace RC1toRC2.Models
{
    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {

The Identity APIs have changed a little so you’ll have fewer extension methods like User.GetUserID() and will have to use the UserManager more often, but those are pretty small changes that you’ll find quickly in upgrading.

MVC Changes

The global search and replace missed a couple of spots in the views. Make sure you search and replace the name spaces in the views too. For example, in the _ViewImports.cshtml, you’ll need to change the TagHelpers and namespaces:

@using Microsoft.AspNetCore.Identity
@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
@inject Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration TelemetryConfiguration

Likewise, the Shared/_LoginPartial.cshtml may be using new Identity functionality (using the SigninManager and the UserManager):

@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager

@if (SignInManager.IsSignedIn(User))
{
    <form asp-controller="Account" asp-action="LogOff" method="post" id="logoutForm" class="navbar-right">
        <ul class="nav navbar-nav navbar-right">
            <li>
                <a asp-controller="Manage" asp-action="Index" title="Manage">Hello @UserManager.GetUserName(User)!</a>
            </li>
            <li>
                <button type="submit" class="btn btn-link navbar-btn navbar-link">Log off</button>
            </li>
        </ul>
    </form>
}

While you should look at all your views for changes in namespaces, one of the key changes in the validation summary. For example, in the default **Login.cshtml **and Register.cshtml, the value of the asp-validation-summary is now just the value of the enum (not the entire name of the enum):

    <div asp-validation-summary="All" class="text-danger"></div>

You’ll probably want to do a search/replace-all on this change across your views (e.g. cshtml files).

More Code Changes

If you build at this point you should build your project and find anything that doesn’t build. Lots of it will be from missing references that Visual Studio will help you find (e.g. EnviromentVariables in configuration is now in a separate nuget package).

Other Changes

Entity Framework Migrations

Last thing once the project is building is to fix the migrations. You can’t do this until the project compiles. It’s not worth trying to fix the migrations, easier to delete them and recreate them. If you have more than one, this is difficult to get the multiple stages you might have. In that case, you’ll need to edit them to fix them, but it’s not trivial. For my case, I’m just deleting them entirely.

You can use the new dotnet command to do it like before:

D:\projects\ASPNETCoreRC1ToRC2\src\RC1toRC2>dotnet ef migrations add InitialDatabase
Project RC1toRC2 (.NETCoreApp,Version=v1.0) will be compiled because inputs were modified
Compiling RC1toRC2 for .NETCoreApp,Version=v1.0
Compilation succeeded.
    0 Warning(s)
    0 Error(s)
Time elapsed 00:00:02.0142435

Done. To undo this action, use 'ef migrations remove'

Now that you have the migrations, you should be close to being done.

LaunchSettings.json

Normally, you would not edit this file manually, but the conversion isn’t changing it and leaving the old dnx stuff. So open up the project properties tree to find the file:

image

Environment variables have changed some names and the separator to work with the configuration system has changed to underscore (from colon) to better interoperate with *nix systems. You’ll want to search and replace the old Development key to the new environment variable:

Hosting:Environment –> ASPNETCORE_ENVIRONMENT

Next, find the ‘web’ section (that was used in RC1):

"web": {
  "commandName": "web",
  "environmentVariables": {
    "ASPNETCORE_ENVIRONMENT": "Development"
  }
}

You can then change it to use the new dotnet stuff:

"RC1toRC2": {
  "commandName": "Project",
  "launchBrowser": true,
  "launchUrl": "http://localhost:5000",
  "environmentVariables": {
    "ASPNETCORE_ENVIRONMENT": "Development"
  }
}

The “commandName” string of “Project” tells it to use “dotnet run” when debugging. the other settings help launch the app when you debug.

Get the Code

I’ve uploaded a test conversion on github:

https://github.com/shawnwildermuth/ASPNETCoreRC1ToRC2

You can see the two commits are before and after here:

Comparing the Commits

Hope this helps!