ASP.NET Core 3.1 - Migrate From .NET Core 2.2

Ken Haggerty
Created 10/01/2019 - Updated 12/08/2019 22:37

This article will demonstrate how to migrate the ASP.NET Core 2.2 - Bootstrap Native Project to .NET Core 3.1. Registered users can download the free ASP.NET Core 2.2 - Bootstrap Native Project and the $5 ASP.NET Core 3.1 - Bootstrap Native Project from Manage > Assets.

Visual Studio 2019 v16.4 or higher which includes the .NET Core 3.1 SDK is required. The MS documentation at Migrate from ASP.NET Core 2.2 to 3.0 and Migrate from ASP.NET Core 3.0 to 3.1 tries to cover all project types and is not very clear on Razor Pages with Identity.

I used a new ASP.NET Core 3.0 Web Application (Razor Pages) project with Identity and the documentation to migrate the ASP.NET Core 2.2 - Bootstrap Native Project to .NET Core 3.0. The upgrade from .NET Core 3.0 to 3.1 was just updating the Target Framework and NuGet packages. When I upgraded KenHaggerty.Com to .NET Core 3.1 I found a couple of issues. You should migrate a copy of your project to analyze and mitigate any issues.

Let's start by editing the project file. Right click on the project then click Edit Project File.

Edit Project File
Edit PROJECTNAME.csproj:
Update the Target Framework:
<TargetFramework>netcoreapp3.1</TargetFramework>
Optionally remove the AspNetCoreHostingModel property if its value is InProcess:
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
If TypeScriptToolsVersion, update version to Latest:
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
Remove obsolete package references:
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />

Now use the Nuget Manager to install new packages. Right click on the project then click Manage NuGet Packages. The Microsoft.AspNetCore.Mvc.NewtonsoftJson package is required for proper Admin > Users > Details serialization. See Migrate from ASP.NET Core 2.2 to 3.0 - Json.NET support

Manage NuGet Packages
On the Browse tab, search and install the following packages:
  • Microsoft. AspNetCore. Diagnostics. EntityFrameworkCore
  • Microsoft. AspNetCore. Identity. EntityFrameworkCore
  • Microsoft. AspNetCore. Mvc. NewtonsoftJson
  • Microsoft. EntityFrameworkCore. SqlServer
  • Microsoft. EntityFrameworkCore. Tools
On the Installed tab, update:
  • Microsoft. VisualStudio. Web .CodeGeneration.Design
NuGet Packages

Update the Program class to use IHostBuilder.

Edit Program.cs, implement IHostBuilder:
public static void Main(string[] args)
{
    CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) => Host
    .CreateDefaultBuilder(args)
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder
        .ConfigureKestrel(serverOptions =>
        {
            // Set properties and call methods on options
        })
        .UseStartup<Startup>();
    });

Update the namespaces with using Microsoft.Extensions.Hosting;.

Use Find and Replace > Current Project to replace IHostingEnvironment with IWebHostEnvironment then update the namespaces with using Microsoft.Extensions.Hosting;.

Edit IdentityHostingStartup.cs in the Areas > Identity > Pages folder, use Remove and Sort Usings to remove the namespace using Microsoft.AspNetCore.Identity.UI;.

Remove And Sort Usings

Update the configuration in the Startup class.

Edit Startup.cs > ConfigureServices:
Remove:
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
    .AddRazorPagesOptions(options =>
    {
        options.AllowAreas = true;
        options.Conventions.AuthorizeAreaFolder("Identity", "/Account/Manage");
        options.Conventions.AuthorizeAreaPage("Identity", "/Account/Logout");
        options.Conventions.AuthorizeAreaPage("Admin", "/Index");
        options.Conventions.AuthorizeAreaFolder("Admin", "/Users");
    });
Add:
services.AddRazorPages(options =>
    {
        options.Conventions.AuthorizeAreaFolder("Identity", "/Account/Manage");
        options.Conventions.AuthorizeAreaPage("Identity", "/Account/Logout");
        options.Conventions.AuthorizeAreaPage("Admin", "/Index");
        options.Conventions.AuthorizeAreaFolder("Admin", "/Users");
    })
    .AddNewtonsoftJson();
Edit Startup.cs > Configure:
Add above app.UseAuthentication:
app.UseRouting();
Add below app.UseAuthentication:
app.UseAuthorization();
Remove:
app.UseMvc();
Add:
app.UseEndpoints(endpoints =>
{
    endpoints.MapRazorPages();
});

Build, run and test.

BootstrapNative Index

Because KenHaggerty.Com implements SignalR, I had to edit the startup configuration and update the SignalR client library.

Edit Startup.cs > ConfigureServices:
Add:
services.AddSignalR();
Edit Startup.cs > Configure:
Remove:
app.UseSignalR(routes =>
{
    routes.MapHub<HUBCLASS>("/pathToHub");
});
Add to app.UseEndpoints:
app.UseEndpoints(endpoints =>
{
    endpoints.MapRazorPages();
    endpoints.MapHub<HUBCLASS>("/pathToHub");
});

If you are not using LibMan, see ASP.NET Core 2.2 - Manage Client-Side Libraries.

Edit libman.json:
Replace:
@aspnet/signalr@1.1.4
With:
@microsoft/signalr@3.1.0
KenHaggerty.Com Issues

When I migrated KenHaggerty.Com I encountered a couple of issues, both involved the predicate in a LINQ query where clause. I was probably not using best practices as described in EF Core - Client vs. Server Evaluation, but they did not throw exceptions in ASP.NET Core 2.2 and Entity Framework Core 2.1.

The first issue was caused by casting an entity’s Enum property instead of the parameter. When MessageType is an Enum and messageTypeInt is an integer, Where(p => (int)p.MessageType == messageTypeInt) throws an exception but Where(p => p.MessageType == (MessageType)messageTypeInt) does not.

The second issue was caused by using a contains method on a populated IList<string> variable. I was using a function to return a list of article ids associated with a keyword tag. The exception stated “The LINQ expression 'Where<Article>(source: DbSet<Article>, predicate: (a) => (Unhandled parameter: __articleIds_0).Contains(a.Id))' could not be translated.” The remedy was updating the function to return List<string>.

You can find more breaking changes at Breaking changes included in EF Core 3.0.

Update 10/02/2019

I updated the AddRazorPages extension's options.Conventions for authorization.

Update 12/05/2019

I updated the article and images to include ASP.NET Core 3.0 to 3.1.

Comment Count = 0

Please log in to comment.

Login Register
Logged in users receive web notifications.
Web Notifications