Special Offer: My C#/.NET Bootcamp Course is out now. Get 10% OFF using the code FRIENDS10.

I’m working on an internal ASP.NET MVC web application. The original version was developed in Summer 2019 using .NET Core 2.2. I now wanted to migrate to .NET Core 3.1 before working on new features.

In this article, I’m going to explain what changes were required for me to migrate my application. There are high-quality, detailed general migration guides available in the MSDN documentation.

Nonetheless, I want to provide an article that features a real use case for a production application that I migrated from .NET Core 2.2. to .NET Core 3.1.

Check the installed .NET Core SDKs

If we want to migrate an existing application to .NET Core 3.1, we need to make sure that we have the .NET Core 3.1 SDK installed. 

You can check what .NET Core SDKs you have by running the following command in the console:

dotnet --list-sdks

Alternatively, you can open the project settings of an existing project to see what versions are available in the dropdown menu (screenshot below).

Install the .NET Core 3.1 SDK

If you already have the .NET Core 3.1 SDK on your machine, you’re ready to migrate your application. Skip this step.

If you do not have the .NET Core 3.1 SDK on your machine, download it from the official website and install it on your computer.

Changing the target framework

Once we’re sure we have the desired .NET Core SDK available, we start by changing the target framework on the application project. Right-click on the project, click on the Properties menu, and select .NET Core 3.1 as the target framework.

Changing the target framework

Updating the framework packages

Next, we want to update all installed Microsoft.* packages using the NuGet package manager to the latest version. We want to make sure that all of the installed packages have version 3.1.x.

Some packages do not have a 3.1.x version. The reason can be that they were integrated into another Microsoft package or into the SDK. In my case, the following three packages were not updated:

  • Microsoft.AspNetCore.App
  • Microsoft.AspNetCore.Razor.Design
  • Microsoft.AspNetCore.Session

The solution is simple: We can remove all of those referenced packages by uninstalling them using the NuGet package manager.

Updated NuGet Packages

The screenshot above shows my result after I updated all dependencies to version 3.1.x.

Configuring the application Startup

There are a few changes we need to make to the Startup.cs file. Within the ConfigureServices method, I changed the compatibility version from 2.2 to 3.0. Later, I ended up deleting the line.

services.AddMvc()
    .AddMvcOptions( options => options.EnableEndpointRouting = false)
    .SetCompatibilityVersion(CompatibilityVersion.Version_3_0);

According to this MSDN article, the compatibility version definition does not have any impact on .NET Core 3.0+ applications.

Within the Configure method, we need to change the method definition from:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)

to:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

It’s the new type that allows us to check the current environment. When we used to check the environment in .NET Core 2.2 using the following line:

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

we need to change that line to the following for .NET Core 3.1 applications:

if (env.EnvironmentName == "Development")
{
    app.UseDeveloperExceptionPage();
}

Configuring routing

With .NET Core 3.1, the endpoint routing mechanism got introduced. If we want to continue using the MVC routing, we need to set an option within the Startup.cs file:

services.AddMvc()
    .AddMvcOptions( options => options.EnableEndpointRouting = false)

This option deactivates the endpoint routing for the configured application. It’s a handy option if you want to migrate your application step-by-step.

In addition to the configuration above, we need to add the following lines at the end of the ConfigureServices methods to enable routing for your existing views and Razor pages to run with .NET Core 3.1:

services.AddRouting();
services.AddControllersWithViews();
services.AddRazorPages();

Within the Configure method, we need to make sure the following lines are in the right order:

app.UseRouting();
app.UseAuthentication();
app.UseMvc(routes =>
{
    routes.MapRoute(
    name: "default",
    template: "{controller=Home}/{action=Index}/{id?}");
}

It is important that routing is added to the pipeline before authentication.

Changing the Program.cs file

With .NET Core 3.1, the definition of the web host has been changed. I do not know much about the details, but I want to show what I needed to change in order to run the application using .NET Core 3.1. If you want to learn more about the generic host builder in ASP .NET Core 3.1 read this article by Shahed Chowdhuri.

The definition for .NET Core 2.2 was:

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

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

I needed to change it to the following definition to make the application run using .NET Core 3.1.:

public static void Main(string[] args)
{         
    var host = Host.CreateDefaultBuilder(args)
        .UseContentRoot(Directory.GetCurrentDirectory())
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder
                .UseKestrel()
                .UseIIS()
                .UseStartup<Startup>();
        }).Build();
    host.Run();
}

Migrating to endpoint routing

As I wrote above, with .NET Core 3.1, Microsoft introduced endpoint routing. As far as I understand, endpoint routing unites all the different routing variations for the different application types.

Migrating to endpoint routing is very simple for an application that only uses a single default route. We change the definition within the Configure method of the Startup.cs file as follows:

app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{    
    endpoints.MapRazorPages();
    endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
}

Conclusion

With all those little changes documented above, we can migrate an existing .NET Core 2.1 application using Razor Pages and MVC to .NET Core 3.1.

For reference, I provide a full listing of my Startup.cs file after the migration:

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

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
            //Register business services
            services.AddSession(options =>
            {
                options.IdleTimeout = TimeSpan.FromMinutes(30);
            });
            services.AddRouting();
            services.AddControllersWithViews();
            services.AddRazorPages();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.EnvironmentName == "Development")
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();
            app.UseSession();

            app.UseRouting();

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapRazorPages();
                endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }

And the Program.cs file:

public class Program
{
    public static void Main(string[] args)
    {         
        var host = Host.CreateDefaultBuilder(args)
            .UseContentRoot(Directory.GetCurrentDirectory())
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder
                .UseKestrel()
                .UseIIS()
                .UseStartup<Startup>();
            }).Build();

        host.Run();
    }
}

 

Claudio Bernasconi

I'm an enthusiastic Software Engineer with a passion for teaching .NET development on YouTube, writing articles about my journey on my blog, and making people smile.