Migrate from ASP.NET Core 2.0 to 2.1

By Rick Anderson

See What's new in ASP.NET Core 2.1 for an overview of the new features in ASP.NET Core 2.1.

This article:

  • Covers the basics of migrating an ASP.NET Core 2.0 app to 2.1.
  • Provides an overview of the changes to the ASP.NET Core web application templates.

A quick way to get an overview of the changes in 2.1 is to:

  • Create an ASP.NET Core 2.0 web app named WebApp1.
  • Commit the WebApp1 in a source control system.
  • Delete WebApp1 and create an ASP.NET Core 2.1 web app named WebApp1 in the same place.
  • Review the changes in the 2.1 version.

This article provides an overview on migration to ASP.NET Core 2.1. It doesn't contain a complete list of all changes needed to migrate to version 2.1. Some projects might require more steps depending on the options selected when the project was created and modifications made to the project.

Update the project file to use 2.1 versions

Update the project file:

  • Change the target framework to .NET Core 2.1 by updating the project file to <TargetFramework>netcoreapp2.1</TargetFramework>.
  • Replace the package reference for Microsoft.AspNetCore.All with a package reference for Microsoft.AspNetCore.App. You may need to add dependencies that were removed from Microsoft.AspNetCore.All. For more information, see Microsoft.AspNetCore.All metapackage for ASP.NET Core 2.0 and Microsoft.AspNetCore.App metapackage for ASP.NET Core 2.1 and later. For apps that target the .NET Framework:
    • Add individual package references instead of a metapackage reference.
    • Update each package reference to 2.1.
  • Remove references to <DotNetCliToolReference> elements for the following packages. These tools are bundled by default in the .NET Core CLI and don't need to be installed separately.
    • Microsoft.DotNet.Watcher.Tools (dotnet watch)
    • Microsoft.EntityFrameworkCore.Tools.DotNet (dotnet ef)
    • Microsoft.Extensions.Caching.SqlConfig.Tools (dotnet sql-cache)
    • Microsoft.Extensions.SecretManager.Tools (dotnet user-secrets)
  • Optional: you can remove the <DotNetCliToolReference> element for Microsoft.VisualStudio.Web.CodeGeneration.Tools. You can replace this tool with a globally installed version by running dotnet tool install -g dotnet-aspnet-codegenerator.
  • For 2.1, a Razor Class Library is the recommended solution to distribute Razor files. If your app uses embedded views, or otherwise relies on runtime compilation of Razor files, add <CopyRefAssembliesToPublishDirectory>true</CopyRefAssembliesToPublishDirectory> to your project file.

The following markup shows the template-generated 2.0 project file:

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>netcoreapp2.0</TargetFramework>
    <UserSecretsId>aspnet-{Project Name}-{GUID}</UserSecretsId>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.All" Version="2.1.4" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.3" PrivateAssets="All" />
    <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.0.4" PrivateAssets="All" />
  </ItemGroup>
  <ItemGroup>
    <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.3" />
    <DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="2.0.2" />
    <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.4" />
  </ItemGroup>
</Project>

The following markup shows the template-generated 2.1 project file:

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

  <PropertyGroup>
    <TargetFramework>netcoreapp2.1</TargetFramework>
    <UserSecretsId>aspnet-{Project Name}-{GUID}</UserSecretsId>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.4" />
    <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.1.1" PrivateAssets="All" />
  </ItemGroup>

</Project>

Rules for projects targeting the shared runtime

ASP.NET Core includes the following shared runtimes:

The version specified by the package reference is the minimum required version. For example, a project referencing the 2.1.1 versions of these packages won't run on a machine with only the 2.1.0 runtime installed.

Known issues for projects targeting the shared runtime:

  • Using Microsoft.AspNetCore.App 2.1.0 with Entity Framework Core 2.1.1 causes NuGet restore failures due to package conflicts. The recommended solution is to upgrade Microsoft.AspNetCore.App to 2.1.1. For more information, see Packages that share dependencies with Microsoft.AspNetCore.App cannot reference patch versions.

  • All projects that must use Microsoft.AspNetCore.All or Microsoft.AspNetCore.App should add a package reference for the package in the project file, even if they contain a project reference to another project using Microsoft.AspNetCore.All or Microsoft.AspNetCore.App.

    Example:

    • MyApp has a package reference to Microsoft.AspNetCore.App.
    • MyApp.Tests has a project reference to MyApp.csproj.

    Add a package reference for Microsoft.AspNetCore.App to MyApp.Tests. For more information, see Integration testing is hard to set up and may break on shared framework servicing.

Update to the 2.1 Docker images

In ASP.NET Core 2.1, the Docker images migrated to the dotnet/dotnet-docker GitHub repository. The following table shows the Docker image and tag changes:

2.0 2.1
microsoft/aspnetcore:2.0 microsoft/dotnet:2.1-aspnetcore-runtime
microsoft/aspnetcore-build:2.0 microsoft/dotnet:2.1-sdk

Change the FROM lines in your Dockerfile to use the new image names and tags in the preceding table's 2.1 column. For more information, see Migrating from aspnetcore docker repos to dotnet.

Changes to Main

The following images show the changes made to the templated generated Program.cs file.

old version differences

The preceding image shows the 2.0 version with the deletions in red.

The following image shows the 2.1 code. The code in green replaced the 2.0 version:

new version differences

The following code shows the 2.1 version of Program.cs:

namespace WebApp1
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>();
    }
}

The new Main replaces the call to BuildWebHost with CreateWebHostBuilder. IWebHostBuilder was added to support a new integration test infrastructure.

Changes to Startup

The following code shows the changes to 2.1 template generated code. All changes are newly added code, except that UseBrowserLink has been removed:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace WebApp1
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });


            services.AddMvc()
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();
            // If the app uses Session or TempData based on Session:
            // app.UseSession();

            app.UseMvc();
        }
    }
}

The preceding code changes are detailed in:

Changes to authentication code

ASP.NET Core 2.1 provides ASP.NET Core Identity as a Razor Class Library (RCL).

The default 2.1 Identity UI doesn't currently provide significant new features over the 2.0 version. Replacing Identity with the RCL package is optional. The advantages to replacing the template generated Identity code with the RCL version include:

  • Many files are moved out of your source tree.
  • Any bug fixes or new features to Identity are included in the Microsoft.AspNetCore.App metapackage. You automatically get the updated Identity when Microsoft.AspNetCore.App is updated.

If you've made non-trivial changes to the template generated Identity code:

  • The preceding advantages probably do not justify converting to the RCL version.
  • You can keep your ASP.NET Core 2.0 Identity code, it's fully supported.

Identity 2.1 exposes endpoints with the Identity area. For example, the follow table shows examples of Identity endpoints that change from 2.0 to 2.1:

2.0 URL 2.1 URL
/Account/Login /Identity/Account/Login
/Account/Logout /Identity/Account/Logout
/Account/Manage /Identity/Account/Manage

Applications that have code using Identity and replace 2.0 Identity UI with the 2.1 Identity Library need to take into account Identity URLs have /Identity segment prepended to the URIs. One way to handle the new Identity endpoints is to set up redirects, for example from /Account/Login to /Identity/Account/Login.

Update Identity to version 2.1

The following options are available to update Identity to 2.1.

  • Use the Identity UI 2.0 code with no changes. Using Identity UI 2.0 code is fully supported. This is a good approach when significant changes have been made to the generated Identity code.
  • Delete your existing Identity 2.0 code and Scaffold Identity into your project. Your project will use the ASP.NET Core Identity Razor Class Library. You can generate code and UI for any of the Identity UI code that you modified. Apply your code changes to the newly scaffolded UI code.
  • Delete your existing Identity 2.0 code and Scaffold Identity into your project with the option to Override all files.

Replace Identity 2.0 UI with the Identity 2.1 Razor Class Library

This section outlines the steps to replace the ASP.NET Core 2.0 template generated Identity code with the ASP.NET Core Identity Razor Class Library. The following steps are for a Razor Pages project, but the approach for an MVC project is similar.

  • Verify the project file is updated to use 2.1 versions
  • Delete the following folders and all the files in them:
    • Controllers
    • Pages/Account/
    • Extensions
  • Build the project.
  • Scaffold Identity into your project:
    • Select the projects exiting _Layout.cshtml file.
    • Select the + icon on the right side of the Data context class. Accept the default name.
    • Select Add to create a new Data context class. Creating a new data context is required for to scaffold. You remove the new data context in the next section.

Update after scaffolding Identity

  • Delete the Identity scaffolder generated IdentityDbContext derived class in the Areas/Identity/Data/ folder.

  • Delete Areas/Identity/IdentityHostingStartup.cs

  • Update the _LoginPartial.cshtml file:

    • Move Pages/_LoginPartial.cshtml to Pages/Shared/_LoginPartial.cshtml
    • Add asp-area="Identity" to the form and anchor links.
    • Update the <form /> element to <form asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Page("/Index", new { area = "" })" method="post" id="logoutForm" class="navbar-right">

    The following code shows the updated _LoginPartial.cshtml file:

    @using Microsoft.AspNetCore.Identity
    
    @inject SignInManager<ApplicationUser> SignInManager
    @inject UserManager<ApplicationUser> UserManager
    
    @if (SignInManager.IsSignedIn(User))
    {
        <form asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Page("/Index", new { area = "" })" method="post" id="logoutForm" class="navbar-right">
            <ul class="nav navbar-nav navbar-right">
                <li>
                    <a asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @UserManager.GetUserName(User)!</a>
                </li>
                <li>
                    <button type="submit" class="btn btn-link navbar-btn navbar-link">Log out</button>
                </li>
            </ul>
        </form>
    }
    else
    {
        <ul class="nav navbar-nav navbar-right">
            <li><a asp-area="Identity" asp-page="/Account/Register">Register</a></li>
            <li><a asp-area="Identity" asp-page="/Account/Login">Log in</a></li>
        </ul>
    }
    

Update ConfigureServices with the following code:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddDefaultIdentity<ApplicationUser>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    services.AddMvc();

    // Register no-op EmailSender used by account confirmation and password reset 
    // during development
    services.AddSingleton<IEmailSender, EmailSender>();
}

Changes to Razor Pages projects Razor files

The layout file

  • Move Pages/_Layout.cshtml to Pages/Shared/_Layout.cshtml

  • The Layout.cshtml file has the following changes:

    • <partial name="_CookieConsentPartial" /> is added. For more information, see GDPR support in ASP.NET Core.
    • jQuery changes from 2.2.0 to 3.3.1

_ValidationScriptsPartial.cshtml

  • Pages/_ValidationScriptsPartial.cshtml moves to Pages/Shared/_ValidationScriptsPartial.cshtml
  • jquery.validate/1.14.0 changes to jquery.validate/1.17.0

New files

The following files are added:

  • Privacy.cshtml
  • Privacy.cshtml.cs

See GDPR support in ASP.NET Core for information on the preceding files.

Changes to MVC projects Razor files

The layout file

The Layout.cshtml file has the following changes:

  • <partial name="_CookieConsentPartial" /> is added.
  • jQuery changes from 2.2.0 to 3.3.1

_ValidationScriptsPartial.cshtml

jquery.validate/1.14.0 changes to jquery.validate/1.17.0

New files and action methods

The following are added:

  • Views/Home/Privacy.cshtml
  • The Privacy action method is added to the Home controller.

See GDPR support in ASP.NET Core for information on the preceding files.

Additional changes