Migrate ASP.NET Core 2.2 system to ASP.NET Core 3.1

Toan Bach Ngoc
7 min readDec 31, 2019

--

This article is the result of migration progress experience when I migrated TEDU.COM.VN to ASP.NET Core version 3.1 from 2.2. So I would like to share what’s happen during migration progress for everyone to refer and help you easier to migrate your system. I saw the differences between migrate a minor version and major version. With major version we often get many breaking changes and incompatibility problems. In here, from 2.x to 3.x is a major version so there are many breaking changes.

First of all, we have to understand what’s changes from 2.2 to 3.1? Exactly, from 2.x to 3.x there are many difference but 2.1 to 2.2 or 3.0 to 3.1 there are no many difference. Because 2.x to 3.x is a major change.

What’s new in .NET Core 3.x

Firstly, before to do anything related to upgrade version, we need to read clearly about the changes? Is there any breaking changes?

- Breaking changes: https://docs.microsoft.com/en-us/dotnet/core/compatibility/2.2-3.1

- What’s new in ASP.NET Core 3.0: https://docs.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-3.0

- What’s new in ASP.NET Core 3.1: https://docs.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-3.1

- What’s new in Entity Framework Core 3.1: https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.0/index

- Entity Framework Core 3.1 breaking changes: https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.0/breaking-changes

Because I don’t upgrade from 2.2 to 3.0 so i will upgrade straightforward to 3.1. If you already have upgraded to 3.0, go to 3.1 is an only small step. Briefly, there are some important points in ASP.NET Core 3.1:

  • Removed Microsoft.AspNetCore.App because it’s added to shared framework.
  • Blazor is a framework build Frontend App on C#
  • Internal communication between Services together with gRPC
  • Update SignalR
  • JSON Serialize built in
  • Separate MVC framework to smaller chunks for specific purpose. Such as: WebAPI or Web MVC app.
  • Improve performance
  • Using Generic Host instead of WebHost

Upgrade steps

I follow only one document from Microsoft: https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.1&tabs=visual-studio

Step 1: Installing .NET Core 3.1 SDK and Global command tools

For development, we have to install the SDK (Software Development Toolkit) version 3.1.100 LTS on homepage: https://dotnet.microsoft.com/download/dotnet-core/3.1.

After that, we will install command tool with the following command:

dotnet tool install --global dotnet-ef --version 3.1.0

This is a tool kit to run dotnet commands on CMD. Example, for checking is there any SDK versions are running on the machine:

dotnet --list-sdks

Similarly, we have — list-runtimes is a command to get list version of .NET Runtime on the machine. On development machine, we need to install SDK and server will install runtime version only.

You can choose download Runtime for Run App and Build App will choose SDK:

Note for .NET Core 3.x, you have to install Visual Studio 2019.

Step 2: Change target framework version on all projects

You need to change TargetFramework on .csproj file for all projects with Visual Studio or Visual Studio Code. I’m using Visual Studio code on Mac. I don’t like Visual Studio on Mac because it’s so bad. We will change .NET Core Version from 2.2 to 3.1 in <TargetFramework> to netcoreapp3.1 and rebuild solution.

After build the solution, i’m sure you will get many error and warning, in my experience, we need to read clearly errors and warning messages from system. It’s often begin from obsoleted library or incompatibility. You can follow by this guide : https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.1&tabs=visual-studio

Step 3: Remove packages no longer needed.

In ASP.NET Core 3.x or later, many libraries such as Microsoft.AspNetCore.App or Microsoft.AspNetCore.Razor.Design will be integrated to Share Framework so we don’t have to add to project as a package.(Follow : https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.1&tabs=visual-studio#remove-obsolete-package-references

Như vậy project sẽ bỏ đi kiểu như sau:

<Project Sdk="Microsoft.NET.Sdk.Web"> 
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework> <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App"/> <PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
</ItemGroup>
</Project>

Go to 3.1 it’s changed to:

<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp3.1</TargetFramework> </PropertyGroup> </Project>

Some packages are no longer declared explicit on 3.x:

Difference between 2.x to 3.x in packages

Step 4: Changes packages version

The other packages we need to change to 3.1.0 version:

<PackageReference Include="Microsoft.AspNetCore.Authentication.Facebook" Version="3.1.0" /> 
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="3.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="3.1.0" />

With Entity Framework Core on Class Library, I also upgrade to 3.1:

<ItemGroup> 
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.0" />
</ItemGroup>

One of the biggest breaking changes in Entity Framework Core 3.x is not automatically execute client evaluation implicit. If your query on LINQ cannot translate to SQL, it will throw an error. If you want to run it, you have to call ToList() or AsEnumerable(). This helps your query will become more explicit and if it cannot execute on SQL, we can see immediately.

After change version for packages, you can re-built solution. If you are still get error, you can workaround by go to every project folder and delete obj folder and run command: dotnet restore.

Step 5: Change some classes

Follow: https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.1&tabs=visual-studio#startup-changes

Some classes changes in ASP.NET Core 3.1:

- Change HostingEnvironment to IWebHostEnvironment (most cases using in Startup.cs)

- Change environment library env.IsDevelopment() to env.ApplicationName == Environments.Development

- In .NET Core 2.x we use service.AddMvc() in ConfigureServices method of Startup.cs, but in ASP.NET Core 3.x, it’s separated to smaller chunks. Example you need use only Web API we don’t need to add View engine. So you need to add services.AddControllers() is enough. For MVC App we need to add services.AddControllersWithViews() instead of add AddMvc() every time.

- For ASP.NET Core 3.x, we can use built in JSON Serialize library better than NewtonSoft.Json (follow by advertise from Microsoft).

services.AddControllers().AddNewtonsoftJson(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver());

- About Configure method in Startup.cs, it separate to Authentication and Authorization clearly, and you have to ensure the order of middle-wares. Please follow: https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.1&tabs=visual-studio#migrate-startupconfigure

Example:

The order of middle-wares is StaticFiles, Routing, Cors, Authentication, Authorization, Endpoints.

- ASP.NET Core 3.x removed SignalR:

app.UseSignalR(routes => { });

Instead of we use UseEndpoint.

Routing configuration will completely put in UseEndpoint instead of UseMvc in previous version. ( There are 2 methods for routing configuration is Attribute and Middleware)

Mapping routes using UseEndpoints middleware

- Configure Swagger for Web API will use OpenAPI:

ConfigureServices:

services.AddSwaggerGen(s => { 
s.SwaggerDoc("v1", new OpenApiInfo {
Version = "v1",
Title = "TEDU Project",
Description = "TEDU API Swagger surface",
Contact = new OpenApiContact { Name = "ToanBN",
Email = "tedu.international@gmail.com",
Url = new Uri("https://www.tedu.com.vn") },
License = new OpenApiLicense { Name = "MIT", Url = new Uri("https://github.com/teduinternational/TEDUapp") }
});
//First we define the security scheme s.AddSecurityDefinition("Bearer",
//Name the security scheme new OpenApiSecurityScheme {
Description = "JWT Authorization header using the Bearer scheme.", Type = SecuritySchemeType.Http,
//We set the scheme type to http since we're using bearer authentication Scheme = "bearer"
//The name of the HTTP Authorization scheme to be used in the Authorization header. In this case "bearer".
});
s.AddSecurityRequirement(new OpenApiSecurityRequirement{ { new OpenApiSecurityScheme{
Reference = new OpenApiReference{ Id = "Bearer", //The name of the previously defined security scheme. Type = ReferenceType.SecurityScheme } },new List<string>() }
});
});

ConfigureService method:

app.UseSwagger(c => { 
c.PreSerializeFilters.Add((document, request) =>
{
var paths = document.Paths.ToDictionary(item => item.Key.ToLowerInvariant(), item => item.Value);

document.Paths.Clear(); foreach (var pathItem in paths) {
document.Paths.Add(pathItem.Key, pathItem.Value); } });
});
// Enable the Swagger UI middleware and the Swagger generator
app.UseSwaggerUI(s => { s.SwaggerEndpoint("/swagger/v1/swagger.json", "Project API v1.1"); });

One more point, with Program.cs file, ASP.NET Core 3.1 used Generic Host is Host instead of WebHost: https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.1&tabs=visual-studio#hostbuilder-replaces-webhostbuilder

Step 6: Run and fix.

There are compile time and runtime error types. So we need to run application and if there are any error, we will fix it. Example: Swashbuckle.AspNetCore has to upgrade to 5.0.0-rc5 or later. Or static files bundling is LigerShark.WebOptimizer.Core not work with newer version. We have to use Gulp: https://docs.microsoft.com/en-us/aspnet/core/client-side/bundling-and-minification?view=aspnetcore-3.1&tabs=visual-studio#consume-bundleconfigjson-from-gulp

You also spend more time to test functions related to Entity Framework Core query

Step 7: Deploy to Windows Server

To deploy on server, you need to install both runtime for .NET Core 3.1 and ASP.NET Core 3.1. Download: https://dotnet.microsoft.com/download/dotnet-core/3.1. You can see the right column ASP.NET core Runtime 3.1.0 and .NET Core Runtime 3.1.0. You have to install both on server.

Conclusion

It’s an upgrade version with many changes. This is a LTS version (Long term support). That is the reason we can upgrade to. Follow the .NET Core roadmap, we will have .NET 5 in the end of 2020 with a unify version for .NET Framework and .NET Core.

After using .NET Core 3.x, i can say it’s quietly small and fast.

Originally published at https://tedu.com.vn.

--

--