.NET Nakama

Should I Stay or Should I Go to .NET 5.0?

January 04, 2021 (~23 Minute Read)
.NET 5 MIGRATION NEWS

Introduction

In a previous article .NET Nakama (2020, September), we have seen that .NET 5 will be the future for the .NET platform and that the vision is to unify all platforms (.NET Core, .NET framework and Xamarin) into one .NET and one toolchain. We already knew from May 2020 that due to COVID-19 pandemic, there will be delays, causing the .NET 5 not to have the unification “one .NET to rule them all”.

There was a lot of criticism about it, some talked about excuses and others talked about broken promises. OK maybe… but nobody could predict a global pandemic and take it into account in the project’s risk management. The pandemic affected all of us, in our daily lives, habits, hobbies, morale, fears, …everything!. I believe that people make the company, so… it is expected for companies to be affected by the pandemic and as a result, to change their priorities and goals. We must have more empathy for others (companies are people), especially in such times. I am sorry for this out-of-scope paragraph, let’s continue to the .NET 5.0 release.

.NET 5.0 is the first Current Release of the.NET unification. That means that it will be supported for three months after .NET 6.0 is released (i.e. until February 2022). The .NET 6.0 will be a Long Term Support (LTS) release, which will be supported for three years (just like .NET Core 3.1).

In this article, we will take a tour on the .NET 5.0 (improvements, performance, new features, etc.) and we will try to decide if we should migrate to .NET 5.0 depending on different cases.

.NET 5.0 Requirements

To start developing using .NET 5.0 we will have to install the .NET 5 SDK or update to the latest Visual Studio 2019 or Visual Studio for Mac, which includes the related SDK. In general, Visual Studio 2019 16.8 or later is required to use .NET 5 on Windows. To use .NET 5 with Visual Studio Code, we will also have to install the latest version of the C# extension (Roth D., 2020 November 10).

To run our web/server applications on our server, we will have to install the ASP.NET Core Runtime. As stated on Microsoft’s website and by Strahl R. (2020, November 25), it is recommended for Windows Servers to install the Hosting Bundle, which includes the .NET Runtime and IIS support.

.NET 5.0 Improvements

Nullability Annotation Improvements

The .NET 5.0 libraries are now completely annotated for nullability (Lander R., 2020 November 10), which is a big step, if we consider that in .NET Core 3.0 around 20% of the framework was annotated for nullability (Carter P., 2019 August 6). This is important for projects in which the Nullable Reference Types (NRTs) feature is enabled because more warning information messages about nulls will be available.

Let’s have a quick reminder of this feature. In C# 8.0, the Nullable Reference Types (NRTs) feature was released, which let us make the flow of nulls, explicit in our code and warn us respectively (Torgersen M., 2019 November 27). In C#, reference types (e.g. classes) are allowed to be null. In addition, we can use them without checks, which may result in NullReferenceException. By enabling the NRT feature, the reference types are split into non-nullable reference types (such as string) and nullable reference types (such as string?) and enforce their null behaviour with warnings (Lander R., 2020 November 10). For more information about the NRTs, read the related documentation and check the related implementations for .NET 5.0.

Regex Performance Improvements

Several optimizations have been performed to the Regex engine, which results in significant throughput improvements, from 3 to 6 times better, and in some cases, much more (Toub S., 2020 April 2). In Toub S. (2020, April 2), you can read in detail the changes that have been performed into System.Text.RegularExpressions in .NET 5 with the related benchmarks comparing to .NET 3.1.

JSON Serialization Improvements

In .NET 5.0, the JsonSerializer of the System.Text.Json library is significantly improved in terms of performance and reliability, have new features included and support more platforms (Akinrinade L., 2020 August 25). The following table presents some of the enhancements that are presented in the related issue.

Category Description Issue
Feature Add extension methods for HttpClient that allow (de)serializing JSON. #32937
Feature Support (de)serializing fields. #876
Feature Support deserializing objects using parameterized constructors. #29895
Feature Support non-string dictionary keys. #30524
Feature Support the new C# record types. #38539
Performance Improve (de)serialization performance for collections, resulting in measurable improvements (~1.15x-1.5x on deserialize, ~1.5x-2.4x+ on serialize) (Toub S., 2020 July 13) (Lander R., 2020 September 13). #2259
Performance Improve serialization performance for small or value-type POCOS (TechEmpower) with an increase of ~19% (Lander R., 2020 September 13), by adding a layer of caching to help retrieve the metadata used internally for the type being (de)serialized (Toub S., 2020 July 13). dotnet/runtime#37976 , #36635
Performance Improve deserialization performance for case-insensitive and missing-property cases (~1.75x faster in some cases). JSON properties are often camelCase and .NET properties and fields are typically PascalCase (Lander R., 2020 September 13). #35848
Performance Improve performance for serialializing long JSON strings (10-20% improvement for end-to-end scenarios). dotnet/corefx#41845
Platform support Enable JsonSerializer to work on Xamarin iOS/Android. #31326

General Performance improvements

I can clearly say that .NET 5.0 is a release full of performance improvements, just take a look of Toub S. (2020, July 13) article to see all cases and numbers. Currently, the ASP.NET Core in .NET 5.0 is the fastest .NET web framework, with good benchmarks in comparison to .NET 3.1 (Roth D., 2020 November 10). For example, the maximum network throughput (in requests per second, RPS) for JSON serialization responses is increased by 22.6% - 44.6% and the mean latency is decreased by 21.1% - 66.7% (Roth D., 2020 November 10).

In addition, all of this work was also performed to provide consistent performance, which is measured using the P95 latency metric. The P95 latency means that 95% of the total evaluated requests have a latency equal or below of the reported value. This is an important metric because it can help us select the appropriate and cheaper host to our applications. In .NET 5.0, the P95 latency has dropped due to refinements in the GC, tiered compilation, and many other areas (Lander R., 2020 November 10).

Languages Improvements (C# and F#)

We have already seen that .NET 5.0 comes with many improvements and new features, but that’s not all. .NET 5.0 comes with C# 9 and F# 5 language improvements.

ASP.NET Core

Kestrel and HTTP/2

  • The HTTP/2 application protocol (Grigorik I., 2013) is improved in .NET 5.0 Kestrel, by supporting the HPack dynamic compression of the response headers. In addition, Kestrel can be configured to send HTTP/2 PING frames to ensure that an idle connection is still functional by configuring the following options: Http2.KeepAlivePingInterval and Http2.KepAlivePingTimeout.
  • Kestrel has improved to detect changes to the configuration (KestrelServerOptions.Configure) and then, unbind from existing endpoints and bind to new endpoints without requiring an application restart.

SignalR Hub Filters and Parallel Hub Invocations

SignalR is an open-source library that enables server-side code to push content to clients instantly (real-time). It uses high-level pipelines named hubs, which allows a client and server to call methods on each other (Remote Procedure Calls, RPC).

The SignalR Hub filters, is a new feature that allows us to run code before and after the hub methods (main code) are called. It is similar to the middleware’s pipeline that we have seen in .NET Nakama (2020, December), in which we can execute code in every HTTP request, before and after our application code (e.g. our API Controller).

Additionally, in .NET 5.0, SignalR can be configured to allow clients to invoke multiple methods at the same time by configuring the MaximumParallelInvocationsPerClient in HubOptions.

HttpClient Εxtension Μethods

Nowadays, the HttpClient and the JsonSerializer is commonly used together to communicate with Web APIs. In .NET 5.0, new extension methods are created to greatly simplify the use of these two library APIs together (see list below). The extension methods, perform the HTTP request, map JSON to our Type and throw an exception if the HTTP response was not successful (EnsureSuccessStatusCode()).

The following source code example is using the new GetFromJsonAsync extension method to get and deserialize the weather forecast JSON data into a Forecast object.

string serviceURL = "https://localhost:5001/WeatherForecast";
HttpClient client = new();
Forecast[] forecasts = await client.GetFromJsonAsync<Forecast[]>(serviceURL);

Control Startup class activation

In .NET 5.0, with the additional UseStartup overload, we can create a factory method for controlling the Startup class activation. It is useful to create different types of the Startup based on our needs (e.g. different dependency injection services and middleware pipeline) and to pass additional parameters to Startup that are initialized along with the host.

Auto Refresh Project and Execute Tests

Are you tired of starting/stopping your ASP.NET Core project or re-running your tests for small changes? In .NET 5, you can use the dotnet watch tool, which launches the default browser, restarts the project when changes are made to the code and auto-refreshes the browser. The dotnet watch tool is used per project, so you have to either specify the project by using the --project option or run it inside the project’s folder. The available commands are:

  • dotnet watch run: Detects file changes, restarts the project and refreshes the browser.
  • dotnet watch test: Detects file changes and reruns the tests.

OpenAPI Specification by Default

As described in OpenAPI Initiative, the

OpenAPI Specification (OAS) defines a standard, programming language-agnostic interface description for REST APIs, which allows both humans and computers to discover and understand the capabilities of a service without requiring access to source code, additional documentation, or inspection of network traffic.

In .NET 5.0, when using the ASP.NET Core API template to create a new project, by default the OpenAPI support is enabled (Figure 1). This is supported by containing a NuGet dependency on Swashbuckle. An open-source project which generates the API documentation and includes a UI to explore and test operations directly from the routes, controllers and models.

The OpenAPI support is enabled by default when creating a new ASP.NET Core web application.
Figure 1. - The OpenAPI support is enabled by default when creating a new ASP.NET Core web application.

The template will generate source code (see Figure 2) in Startup.ConfigureServices, which will activate the OpenAPI document generation and in the Startup.Configure, which will add Swashbuckle middleware to generate and serve the Swagger UI page in development mode.

The generated code by regarding OpenAPI support.
Figure 2. - The generated code by regarding OpenAPI support.

Blazor

In .NET 5.0, Blazor received a lot of attention to significantly improve the performance of the WebAssembly runtime (2x to 3x faster) and also add missing features. Some of the new features and improvements are presented in the following list, to see the full list click here:

  • CSS isolation: A feature that was requested from many developers in order to define CSS styles that are scoped to a given component.
  • InputFile component: This new component allows reading one or more files selected by a user for upload.
  • InputRadio and InputRadioGroup components: New components that simplify data binding to radio button groups with integrated validation.
  • Lazy load assemblies: Improve startup performance by loading some application assemblies until they are required.
  • Component virtualization: A technique for limiting UI rendering to just the parts that are currently visible.
  • Protected Browser Storage (for Blazor Server): Blazor provides built-in support to store the application’s state encrypted in the browser, to reduce the potential risk of tampering the stored data.

Application Deployment

Single-file Applications

Single-file applications can be produced with .NET Core 3.1, which packages binaries into a single file and then, unpacks those files to a temporary directory to load and execute them. In .NET 5.0, we can create single-file applications, containing its dependencies, which are loaded directly into memory.

But, this is only the beginning of this feature, because for now, it is only available on Linux. Due to various constraints on Windows or macOS, this process is more complicated than anticipated. As Lander R. (2020, November 10) stated, they will revisit this situation again in .NET 6.

The single-file applications can be:

  • Framework-dependent single-file applications (relying on a globally-installed .NET runtime):
dotnet publish -r linux-x64 --self-contained false /p:PublishSingleFile=true
  • Self-contained single-file application (carrying the runtime, thus are larger):
dotnet publish -r linux-x64 --self-contained true /p:PublishSingleFile=true

To reduce the size of your application you can perform trimming of the unused assemblies. This is a risky process and needs a lot of testing to ensure that everything is working (e.g. by using an assembly that is loaded on runtime).

To improve your application’s startup time and latency, you can compile your application as ReadyToRun (R2R) by setting to true the PublishReadyToRun flag in the aforementioned dotnet publish commands.

Containers

We can see that Microsoft is investing in containers in multiple ways, for example, we can easily enable Docker support from VS 2019. In addition, there are tools currently developing, such as the .NET Monitor tool to collect diagnostic information and the dotnet/tye tool that makes developing, testing, and deploying microservices and distributed applications easier.

When working with containers, we try to keep the size of the images as small as possible. To build and publish a Dockerfile for an ASP.NET Core application, it requires to pull the entire .NET Core SDK and the ASP.NET Core image. In .NET 5.0, the SDK image size is reduced (-36 MB for Ubuntu 20.04) and the ASP.NET Core image size is reduced to 10KB (-64 MB) (Lander R, 2020 April 7).

Besides, in .NET 5.0 the OpenTelemetry is supported to easily work with container orchestrators and capture distributed telemetry data, such as traces, metrics, and logs. OpenTelemetry is a set of APIs, SDKs, tooling and integrations that are designed for the creation and management of telemetry data.

Upgrade Existing Projects to .NET 5.0?

We have seen a lot of new and cool stuff about .NET 5.0: increased performance, improvements in deployment (e.g. single-file applications, containers), improvements in C# 9.0 and F# languages, and blah blah blah .NET 5.0, but… let’s cut to the chase, because the main question remains:

Should I Stay or Should I Go to .NET 5.0?

The answer to this question and almost in every such question in our field is “it depends”. I know that you didn’t want to read that but, that’s the truth. It depends on many parameters, such as our current framework, project type, dependencies, etc.

To make our decision easier, let’s start by collecting the available relative information and identify our alternatives. If you believe that we could include more relative information, please leave a comment at the end of the article 😉.

  • .NET 5.0 enables a much larger group of developers to migrate their .NET Framework code and apps to .NET 5.0 (Lander R., 2020 November 10).
    • But, we have to take into account that .NET 5.0 (and previous .NET Core) stops supporting: ASP.NET WebForms, Windows Communication Foundation (WCF) server and Windows workflow.
  • .NET 5.0 is not a Long Term Support (LTS) release, which raises questions that we have to consider:
    • Does your organization have a relative LTS policy (e.g. use only LTS releases)?
    • The .NET LTS releases will be released every 2 years. Will your organization provide you the time to make bigger migrations every 2 years or it would be better for you to split the effort?
  • .NET 5.0 is already tested by being hosted for months at dot.net and Bing.com (bing.com version) (Lander R., 2020 November 10).
  • Migrating from .NET Framework (especially with legacy features) will require work depending on your project size.
  • Migrating from .NET 3.1 seems to be an easy task, as it would not require code changes in most reported cases. The process to migrate from .NET 3.1 is to a) change the TargetFramework to net5.0 and b) update the NuGet packages. You can follow the related documentation for your case.
    • Strahl R. (2020, November 25) upgraded his personal/company .NET Core 3.1 applications to .NET 5.0 and as he states: It’s been the easiest migration of any major .NET version update that I can remember.
    • Smith S. (2020, October 2) upgraded their ASP.NET 3.1 website to .NET 5.0 (RC1), updated the Azure DevOps pipelines, and deployed it to an Azure app service. Their response is: This was surprisingly easy, taking less than 10 minutes to update .NET 5 - with no compile errors.
  • Breaking changes exists! Read the list of breaking changes in .NET 5.0 that might affect you.
  • Check the unsolved .NET 5 Known Issues.
  • Our alternatives, depending on our case can be to:
    • Stay in our current framework (no migration for us!).
    • Upgrade to .NET 3.1.
    • Wait for .NET 6.0 (LTS).

In the following table, I would try to summarize some of the possible current cases using .NET Core and recommend possible actions per case. As we have already seen, there is no silver bullet. You will have to make a decision based on your project, framework, dependences, LTS policy, breaking changes, available time, etc. You can also read Rockford Lhotka (2020) article for another opinion regarding the migration to .NET 5.0.

Migrating from .NET Framework (especially with legacy features) will require work depending on your project size. Do not panic about the migration! I would recommend you to start by checking your project’s portability by using Microsoft’s Portability Analyzer tool. In addition, read more about the not-supported features (legacy features) in the .NET Nakama (2020, September). Then, make a plan with your product owner and have specific reasons and goals for this migration.

Current Case Upgrade to .NET 3.1 (LTS) Upgrade to .NET 5.0 Wait for .NET 6.0 (LTS)
.NET Core 2.2 I would recommend to firstly migrating to .NET 3.1, where you can easily find relative documentation. When you are stable, you can re-evaluate further upgrading. You could select the direct migration if you immediately need the improvements and new features. Keep in mind that upgrading multiple versions may require additional time and have a higher risk. I would not recommend waiting for .NET 6.0 as there is .NET 3.1 (LST) release available.
.NET Core 3.1 - The migration seems easy. If there is no deterrent, you could spend some time to try migrating in a git-branch. With a small effort, you will gain all the aforementioned improvements. This could be a good decision, especially if your organization has a strict LTS policy, or you know that you will have the available time to make bigger migrations.
.NET Core 3.1 (Containerized) - In addition to the comment above, the advantages of lower CPU usage, memory usage and images sizes are very tempting to select the migration. See comment above!
.NET Core 3.1 (Blazor) - I would recommend migrating to .NET 5.0, as there are a lot of features (that are missing from 3.1) and large improvements in performance. You could wait. I don’t know why though! If there is a reason for your cases to wait, please add a comment below.

Summary

If you are reading this, you should be as excited as me about the release of .NET 5.0 and the future of .NET in general. The .NET 5.0 release, includes a lot of throughput improvements in commonly used libraries such as Regex (3 to 6 times better) and JSON serialization. In addition, .NET 5.0 provides consistent performance, which is measured using the P95 latency metric.

The .NET 5.0 release comes with many C# 9 and F# 5 languages improvements. C# 9.0 offers top-level programs, records, pattern matching enhancements, and many more. F# 5 offers interactive programming and a performance boost for functional programming on .NET.

Currently, the ASP.NET Core in .NET 5.0 is the fastest .NET web framework (with an increase of throughput 22.6% - 44.6% in RPS and with a decrease of latency 21.1% - 66.7%). The HTTP/2 application protocol is improved by supporting the HPack dynamic compression of the response headers. The SignalR Hub filters feature is introduced, which allows us to run code before and after the hub methods (main code) are called. The dotnet watch tool was introduced which detects the changed files, restarts the project and refresh the browser. Blazor received a lot of attention with significant improvements to the performance of the WebAssembly runtime (2x to 3x faster) and also by adding missing features.

In .NET 5.0, a new kind of single-file applications was introduced, containing its dependencies, which are loaded directly into memory but, it is only available on Linux. It is worth mentioning that the size of the required container image is reduced significantly. The SDK image size is reduced (-36 MB for Ubuntu 20.04) and the ASP.NET Core image size is reduced to 10KB (-64 MB).

We saw that it is not that simple to decide to migrate to .NET 5.0. We have to make a decision based on the project, framework, dependences, LTS policy, breaking changes, available time, etc. For that purpose, we have collected the available relative information and identified our alternatives. Finally, we have summarized some of the possible current cases and recommend possible actions per case.

If you have made the migration to .NET 5.0 or you are still not sure what to do, please share your experience and thoughts with us.

References

If you liked this article (or not), do not hesitate to leave comments, questions, suggestions, complaints, or just say Hi in the section below. Don't be a stranger 😉!

Dont't forget to follow my feed and be a .NET Nakama. Have a nice day 😁.