Migration from ASP.NET Core RC1 to RC 2

ASP.NET Core release candidate 2 was released on May 16. The announcement can be found here. One reason for there being so much time between RC 1 and RC 2 is the dnvm, dnx and dnu tool set that ASP.NET Core had been built upon has been replaced by the .NET CLI. To replace the underpinnings of a system at the RC stage is a big under taking, but it was an important step to keep the a consistent set of tools across .NET’s cross-platform offerings.

Installation

With this release Microsoft added http://dot.net to better centralize acquisition of .NET. For ASP.NET Core use the download .NET Core or use this link to go directly to the install directions. If you are using Visual Studio you can get the MSI install from here.

After the installer is complete open a command prompt and run the command dotnet –version to verify version 1.0.0-preview1-002702 is installed.

Global.json

Only change here is a version update with the new value matching the following.

{
  "projects": [ "src", "test" ],
  "sdk": {
    "version": "1.0.0-preview1-002702"
  }
}

Project.json

The changes to this file were so extensive I found it easy to crate a new web application and copy the details out of the new project instead of tried to deal with editing my existing file. The only thing I kept from the original was my userSecretsId. If you do go this route make sure you change the schema (drop down at the top of the editor) to http://json.schemastore.org/project. The following is my resulting file.

{
  "userSecretsId": "replace with your secrets Id",

  "dependencies": {
    "Microsoft.NETCore.App": {
      "version": "1.0.0-rc2-3002702",
      "type": "platform"
    },
    "Microsoft.AspNetCore.Authentication.Cookies": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Diagnostics": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Mvc": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Razor.Tools": {
      "version": "1.0.0-preview1-final",
      "type": "build"
    },
    "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Server.Kestrel": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.StaticFiles": "1.0.0-rc2-final",
    "Microsoft.EntityFrameworkCore.SqlServer": "1.0.0-rc2-final",
    "Microsoft.EntityFrameworkCore.Tools": {
      "version": "1.0.0-preview1-final",
      "type": "build"
    },
    "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0-rc2-final",
    "Microsoft.Extensions.Configuration.Json": "1.0.0-rc2-final",
    "Microsoft.Extensions.Configuration.UserSecrets": "1.0.0-rc2-final",
    "Microsoft.Extensions.Logging": "1.0.0-rc2-final",
    "Microsoft.Extensions.Logging.Console": "1.0.0-rc2-final",
    "Microsoft.Extensions.Logging.Debug": "1.0.0-rc2-final",
    "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0-rc2-final",
    "Microsoft.VisualStudio.Web.CodeGeneration.Tools": {
      "version": "1.0.0-preview1-final",
      "type": "build"
    },
    "Microsoft.VisualStudio.Web.CodeGenerators.Mvc": {
      "version": "1.0.0-preview1-final",
      "type": "build"
    }
  },

  "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"
      ]
    }
  },

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

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

  "runtimeOptions": {
    "gcServer": true
  },

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

  "scripts": {
    "prepublish": [ "npm install", "bower install", "gulp clean", "gulp min" ],
    "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
  }
}

Namespace changes

The following are the namespace changes I hit. There is a pattern so if you hit any listed here it you should be able to make a good guess at what the new name is with AspNet going to AspNetCore and EntityFramework going to EntityFrameworkCore being the most common changes.

Old Namespace New Namespace
Microsoft.AspNet.Http.Authentication Microsoft.AspNetCore.Http.Authentication
Microsoft.AspNet.Authorization Microsoft.AspNetCore.Authorization
Microsoft.AspNet.Builder Microsoft.AspNetCore.Builder
Microsoft.AspNet.Identity Microsoft.AspNetCore.Identity
Microsoft.AspNet.Identity.EntityFramework Microsoft.AspNetCore.Identity.EntityFrameworkCore
Microsoft.AspNet.Hosting Microsoft.AspNetCore.Hosting
Microsoft.AspNet.Http Microsoft.AspNetCore.Http
Microsoft.AspNet.Http.Authentication Microsoft.AspNetCore.Http.Authentication
Microsoft.AspNet.Mvc Microsoft.AspNetCore.Mvc
Microsoft.AspNet.Mvc.TagHelpers Microsoft.AspNetCore.Mvc.TagHelpers
Microsoft.AspNet.Mvc.Rendering Microsoft.AspNetCore.Mvc.Rendering
Microsoft.Data.Entity Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Metadata.Internal
Microsoft.Data.Entity.Infrastructure Microsoft.EntityFrameworkCore.Infrastructure
Microsoft.Data.Entity.Metadata Microsoft.EntityFrameworkCore.Metadata
Microsoft.Data.Entity.Migrations Microsoft.EntityFrameworkCore.Migrations
System.Security.Claims Microsoft.AspNetCore.Identity

Application Entry Point

With the change to the CLI the Main function that was in the StartUp class is no longer sufficient. Using a new project as an example I removed Main from the StartUp and added a new Program class with that now contains the Main function for the application.

using System.IO;
using Microsoft.AspNetCore.Hosting;

namespace ASP.NET_Core_SPAs
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = new WebHostBuilder()
                .UseKestrel()
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseIISIntegration()
                .UseStartup<Startup>()
                .Build();

            host.Run();
        }
    }
}

It is my understanding that all of the above was happening in the previous versions it has just now been made explicit.

StartUp Class

The StartUp had the second most changes after project.json. First as mention above the Main function was deleted. Next in the Startup function a call needs to be made to set the base path on the configuration builder. I missed the new SetBasePath(env.ContentRootPath) call at first and it caused an exception when builder.AddUserSecrets() was called.

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

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

Next in the ConfigureServices function the need to add entity framework and add SQL server is now gone. Also notice that the retrieval of the connection string has changed as well.

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

After:
services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddDbContext<ContactsDbContext>(options =>
    options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

Finally in the Configure function the following should be removed as it is now handled via the UseIISIntegration() call in the application’s Main function.

app.UseIISPlatformHandler(options => options.AuthenticationDescriptions.Clear());

Appsettings.json

The first change in appsettings.json is for the connection string settings mention above.

Before:
"Data": {
     "DefaultConnection": {
       "ConnectionString": "your connection string"
     }
}

After:
"ConnectionStrings": {
    "DefaultConnection": "your connection string"
}

Then the default logging level was changed from verbose to debug.

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

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

Misc Changes in Various Controllers

The return values for HTTP statuses changed by dropping the Http prefix. The following are a few examples.

Old New
HttpBadRequest BadRequest
HttpNotFound NotFound
new HttpStatusCodeResult(StatusCodes.Status409Conflict) new StatusCodeResult(StatusCodes.Status409Conflict)

Getting access to the user ID form the current context now requires access to an instance of the UserManager class which needs to be injected via the constructor.

private readonly ContactsDbContext _context;
private readonly UserManager<ApplicationUser> _userManager;
  
public ContactsApiController(UserManager<ApplicationUser> userManager, 
                             ContactsDbContext context)
{
    _userManager = userManager;
    _context = context;
}

The following shows the difference in usage before and after the update.

Before:
return _context.Contacts.Where(c => c.UserId == User.GetUserId());

After:
return _context.Contacts.Where(c => c.UserId == _userManager.GetUserId(User));

More user ID related changes.

Before:
_userManager.FindByIdAsync(HttpContext.User.GetUserId())

After:
_userManager.GetUserAsync(HttpContext.User)

External principal is now just principal.

Before:
var email = info.ExternalPrincipal.FindFirstValue(ClaimTypes.Email);

After:
var email = info.Principal.FindFirstValue(ClaimTypes.Email);

Is signed in moved from user to the sign in manager.

Before:
User.IsSignedIn()

After:
_signInManager.IsSignedIn(User))

Validation Summary

The validation summary tag helper drop the ValidationSummary prefix.

Before:
<div asp-validation-summary="ValidationSummary.All" class="text-danger"></div>

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

This of course applies to all usages of the validation summary tag helper not just the all case.

Entity Framework

The constructor of a child of DbContext should now accept options.

Before:
public ContactsDbContext()

After:
public ContactsDbContext(DbContextOptions<ContactsDbContext> options)
    : base(options)

There were changes to how tables get named with this version and if you try to run with out the following code entity framework will be unable to locate your tables. By adding the following to OnModelCreating your models will be force back to the naming scheme from RC1.

foreach (var entity in builder.Model.GetEntityTypes())
{
    entity.Relational().TableName = entity.DisplayName();
}

Other Resources

The above should get your project up and running. The changes I have covered here have been posted to my SPA sample application which can be found here and all the change I made are in this commit which should prove useful if I missed anything. During my migration a newly crated project proved to be my greatest resource.

Microsoft provided the following three migrations posts which are also very helpful.

There are also a number of people in the communitity that have also posted guides such as:

One Reply to “Migration from ASP.NET Core RC1 to RC 2”

  1. Pingback: ASP.NET Blog

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.