Introduction

This article is a logical continuation of upgrading the nopCommerce project - a free open-source CMS for creating online stores. Last time we talked about our experience in migrating nopCommerce from ASP.NET MVC to ASP.NET Core 2.2. In this article, we will look at the migration to .NET Core 3.1.

.NET Core 3.1 will be officially supported until December 2022, so migration is a hot topic right now. If you want to take full advantage of the updated framework, keep up with technological innovations, and the growing global trends then it's time to start the migration.

Challenges to be solved in the process of migration to .Net Core 3.1

For the fast-growing eCommerce project, it is imperative to pay great attention to system performance and security. In the first .NET Core 3.0 review, it was announced that the new version of the framework will be much more fast and productive. Implementing the following .Net Core 3.1 features push nopCommerce to the new level of scalability, performance and security:
  1. Tiered compilation allows us to decrease startup time. 
  2. The new built-in high-performance and low-memory support for JSON
  3. The endpoint routing introduced in .NET Core 2.2 has been improved. The main advantage is that the route is now determined before running the middleware. 
At the time of the release, our expectations were confirmed in practice. 
Next, let’s see what exactly affected the nopCommerce performance and how .NET Core evolved since the release of version 2.2.

What's new in .NET Core 3.1

Let's take a closer look at the .NET Core 3.1 innovations that we use in nopCommerce. You can find the detailed instructions on how to migrate from .NET Core 2.2 to .NET Core 3.1 on the official Microsoft website. In this article we will look at the benefits we receive using these innovations.
Generic Host
In .NET Core 2.1 Generic Host is an addition to the Web Host. It allows you to use tools such as dependency injection (DI) and logging abstractions. .NET Core 3 emphasized the greater compatibility with Generic Host, so you can now use the updated Generic Host Builder instead of Web Host Builder.
This allows to create any kind of application, from console applications and WPF to web applications, on the same basic hosting paradigm with the same common abstractions.
public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args)
        {
            return Host.CreateDefaultBuilder(args)
                .UseServiceProviderFactory(new AutofacServiceProviderFactory())
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder
                        .UseStartup<Startup>();
                });
        }
    }
You can still continue to use WebHostBuilder but you need to understand that some types are deprecated in ASP.NET Core 3.1, and may be replaced in the next version.

global.json

The idea and possibility of using this feature already existed in the .NET Core 2.0 version, but its capabilities were incomplete. It was only possible to specify the version of the SDK that your application is using.
More flexible SDK version control became available only in .NET Core 3.0 with the introduction of policies such as allowPrerelease and rollForward. The code below illustrates the use of the .NET Core SDK roll-forward policies.
{
  "sdk": {
    "version": "3.1.201",
    "rollForward": "latestFeature",
    "allowPrerelease": false
  }
}
You can see the full version of the application code in our repository on GitHub.
Now you can specify the version from which you can easily build the application, without having to edit global.json every time after the next patch is released. This way you can define the range of functionality that you need based on your requirements.
It will also give you a guarantee that users of your application will run it exactly on those SDK assemblies that you have defined, and not the latest version installed on their server.

ASP.NET Core Module V2
Prior to .NET Core 2.2, IIS hosted a .NET Core app by executing a Kestrel instance and forwarding requests from IIS to Kestrel by default. Basically IIS acted like a proxy. This works but is slow because there is a double hop from IIS to Kestrel while processing the request. This hosting method is called «OutOfProcess».
.NET Core 2.2 introduced the new "InProcess" hosting model. Instead of forwarding requests to Kestrel, IIS serves requests inside itself. This makes the requests processing much faster because you don't need to forward the request to Kestrel.
However, this was an optional feature and was not used by default.
In .NET Core 3.1, the in-process hosting model is already configured by default, which significantly improves the throughput of ASP.NET Core requests in IIS.
During testing, we recorded a significant increase in system performance with a large number of requests.
<PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <Copyright>Copyright (c) Nop Solutions, Ltd</Copyright>
    <Company>Nop Solutions, Ltd</Company>
    <Authors>Nop Solutions, Ltd</Authors>
    <Version>4.4.0.0</Version>
    <Description>Nop.Web is also an MVC web application project, a presentation layer for public store and admin area.</Description>
    <PackageLicenseUrl>https://www.nopcommerce.com/license</PackageLicenseUrl>
    <PackageProjectUrl>https://www.nopcommerce.com/</PackageProjectUrl>
    <RepositoryUrl>https://github.com/nopSolutions/nopCommerce</RepositoryUrl>
    <RepositoryType>Git</RepositoryType>
    <!--Set this parameter to true to get the dlls copied from the NuGet cache to the output of your project-->
    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
    <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
    <!--When true, compiles and emits the Razor assembly as part of publishing the project-->
    <RazorCompileOnPublish>false</RazorCompileOnPublish>
  </PropertyGroup>
Note that running the application on Linux will still use the out-of-process web server hosting model.
Also, if you host multiple in-process applications on your server, you must ensure that each such application has its own pool.
Endpoint Routing
In .NET Core 2.1, routing was done in Middleware (ASP.NET Core MVC middleware) at the end of the HTTP request pipeline. This means that information about the route, such as what controller action will be taken, was not available for the middleware that processed the request before the MVC middleware in the request pipeline.
Starting with .NET Core 2.2, a new endpoint-based routing system was introduced. This routing concept addresses the aforementioned issues.
Endpoint Routing is now built differently in .NET Core 3.1. The routing phase is separated from the call to the endpoint. This way we have two intermediate middlewares: