.NET Upgrade - A Conceptual Guide
.NET Upgrade - A Conceptual Guide

Migrating to the Latest .NET – A Conceptual Guide

This article is bit late to the party. The .NET 5 support ended on 10 May 2022. So it is high time, that the applications which are on .NET 5 , should be migrated to .NET 6 if it is not done already.

This article is to provide very high level conceptual information about how to perform this migration to any latest .NET version. This article does not explain the usage of .NET Upgrade Assistant. We can discuss that in some future blogpost.

This is not a step by step guide. It is hard to write exact steps for such migrations as many times the exact steps depend on many different things like

  • Third party dependencies (sometimes, upgrading them may need some refactoring)
  • Application Design and (sometimes may decide the order in which components should be migrated)
  • The features that you want to use from the new version of .NET and when do you want to make the changes (during migration activity vs after migration activity).
  • Whether application uses Entity Framework (or other ORM) or not, etc.
  • Whether the application uses WinForms, WPF, Blazor or Razor pages.

This article will provide a generic conceptual guidance. And that’s why, some parts of this article would still be relevant if you want to migrate from .NET 6 to any future versions.

What are the activities involved in this upgrade ?

This section lists the activities that you need to do for upgrading the projects to latest .NET version. Some of these changes (like first 3 from the list) are required to be done in the project file (CSPROJ file). Some changes may be required in the code mostly due to breaking changes or due to some features going obsolete.

  • Updating target framework in CSPROJ files
  • Updating C# language version in CSPROJ files (optional)
  • Analyzing and Upgrading NuGet packages
  • Fix Breaking Changes
  • Build, Run and Test to verify application is functioning as expected
  • Last optional step is to enable new features introduced in the latest .NET version.

Let’s see how to perform these activities.

Prerequisite

Ensure that the latest .NET SDK is installed on developer’s machine. It may also be needed on build servers / other services in dev / test / staging or production environment.

If you are using Kubernetes / Docker, the migration process would involve updating dockerfiles. We need to modify the docker image names and use the container images which has the latest .NET runtime installed.

Update Target Framework

Let’s say you have the solution open in Visual Studio (or Rider). Let’s say you want to migrate all the projects in that solution. Then in every project file (CSPROJ file), we will need to update the target framework setting.

From Visual Studio, we can right click on the project file and select properties. On project properties screen, we can look for Target Framework setting. Select the latest .NET version in the dropdown and hit the save button to save this modified setting to the project file.

As I said earlier, this needs to be done in every project file in the solution. So, if you have 50-100 projects in the solution, you will have to do this 100 times.

Do you want to avoid manual work ? Well, there are some options:

First option is to use Find and Replace functionality in Visual Studio / Visual Studio Code / Notepad++. Open any CSPROJ file and we can fined TargetFramework element in it.

<TargetFramework>net5.0</TargetFramework>

Copy that line and change the target framework moniker (TFM). For .NET 6, the TFM is net6.0 (or it begins with net6.0) and for .NET 7, the TFM is net7.0.

We can optionally choose to use regular expressions for matching the pattern. We can also instruct Find and Replace to replace only on files with csproj extension.

Upgrade to Latest .NET Version – Find and Replace Target Framework Version

Update Language Version (optional)

The latest C# compiler defines the default language version depending on the target framework of the project.

C# 10 is supported only on .NET 6 and newer versions. C# 9 is supported only on .NET 5 and newer versions. C# 8.0 is supported only on .NET Core 3.x and newer versions.

Visual Studio does not provide any UI to update the language version. But we can update the CSPROJ file and change the language version setting if we really need it. CSPROJ file may contain the LangVersion XML element and it can be set to any version. Various supported values for this element are listed here in the documentation.

<LangVersion>10.0</LangVersion>

It is better to not set this explicitly. This is to ensure that you do not use the language features which are not supported in the target framework of the project. But in case you still want to set this setting, you can manually edit the CSPROJ file.

Alternatively, you can also set LangVersion compiler option or you can set this LangVersion for multiple projects in a subdirectory.

Upgrading NuGet Packages

First two steps can be done in one go by using Find and Replace. This one is not that simple.

An application may be dependent on some public NuGet packages (like AutoMapper, or Fluent Validation). The same application may be dependent on internal NuGet packages if the project has an internal NuGet package repository.

In this step, we need to analyze existing packages to figure out which package versions support the intended target .NET version. We also need to analyze the breaking changes, meaning the code modifications which are mandatory if we upgrade the concerned NuGet package.

If there are multiple versions which support the intended .NET version, then I would suggest to use the version which has no (or lesser) breaking changes. By not clubbing other modifications with .NET upgrade pull request, the verifications part becomes a little easier in my opinion. If you want you can upgrade the packages with breaking changes in the subsequent pull requests.

In visual studio, we can right click on the solution and select Manage NuGet packages for Solution option. Then go to Updates tab to view which all packages need upgrade. Select any one package from the list.

The right side will show projects in which that package is used. Select the projects where that NuGet package is installed and hit the Install button.

Manage NuGet Packages for Solution

Breaking Changes

When we upgrade the .NET target framework, most of the times, we may have to do some code modifications. Some code from existing solution may not be compatible with the latest .NET.

Sometimes, there are options to disable new behavior. So, we can choose to disable the new behavior. Sometimes there is no option to disable old behavior, meaning the code change is mandatory.

There can also be some classes which are obsolete. So, although the code works with those classes / APIs, compiler may generate warnings during compilation, suggesting to change the implementation. In such cases, we can keep the warning and fix it in subsequent PRs.

One of the important breaking change from .NET 6 was – Generate error for duplicate files in publish output. Sometimes if we have configuration files in referred projects with same name, build order was replacing the files with same name.

Starting from .NET 6, it does not. It throws the error. This is of course the default behavior which can be disabled. If possible you can also fix the error by removing copy always flag from the files which are not really required in the build output.

Another example, RNGCryptoServiceProvider is marked as obsolete, starting in .NET 6. Using it in code generates warning SYSLIB0023 at compile time.

You can find similar examples while performing upgrade. Depending on needs of application, we may have to decide the priority for fixing such issues.

Optional Latest Features

With .NET 6, C# 10 is the default language version. C# 10 supports many features like top level statements, global implicit usings, File-Scoped namespaces etc. Starting from .NET 6, Nullable Reference Types feature is also going to be enabled by default for the new projects. For ASP .NET Core APIs, there are other features offered like minimal APIs.

When upgrading an existing codebase to latest .NET version, I think it is better to keep these optional modifications separate from actual .NET upgrade. The optional features can be incorporated in subsequent pull requests, one by one.

Also, before incorporating any optional latest features, I think it is better to have discussions within the teams and have the team agreements on coding guidelines in place. These agreements would help to keep the codebase consistent.

With some latest features, there are various syntax for writing, let’s say conditional statements, you can use inequality operator for comparing with null or you can use is not keywords. In order to ensure the consistency in application’s codebase, it is better to have team agreements in place.

Also, enabling some features like NRT (Nullable Reference Types) may have bigger impact and may need careful analysis and implementation. We will discuss about enabling NRT in the coming weeks.

Wrapping Up

We have seen which all activities are required for .NET upgrade. I hope you find this information helpful. Let me know your thoughts.

Leave a ReplyCancel reply