What are the .NET 5 headliners?
Like I said earlier, .NET 5 is primarily the next release after .NET Core 3.1. The version number of 5 was chosen to avoid confusion with .NET Framework 4.x and .NET Core versions. .NET 5 is now the main implementation as a single .NET version stream for all the future releases of .NET. It will be released as .NET 5, .NET 6, .NET 7, and so on, since it has merged the two streams of .NET Framework and .NET Core into one. Therefore, .NET 5 supports more types of apps and platforms than .NET Core or .NET Framework.
Having an edge over .NET Core 3.1 and .NET Framework, .NET 5 has made considerable improvements in many areas, including the following:
- Single-file, self-contained app: This feature has been improved further, as provided earlier by .NET 3.1.
- App trimming and container size optimization: The size of the app has been reduced by trimming the types used inside the .NET libraries and reducing the size of the runtime binaries for containers just to what is essential.
- C# compiler enhancements and C# 9 as well as F# 5.0: Various new features have been added and performance improved further.
- Windows ARM64: ARM64 platform-specific optimizations in the .NET libraries.
- Performance improvements: Applied in many areas, including text, JSON, tasks, and more.
We have now learned the purpose of .NET and seen its main headliners. We will dive deep into various performance improvements and C# 9 features to further increase our knowledge and command over .NET 5 in the next main section. For now, let's get to know some more about some of its major features.
Discovering self-contained applications
To run .NET programs on a certain setup (such as a bare metal machine, VM, or container), .NET runtime is required. Prior to .NET Core 3.0, all the previous versions were dependent on the runtime pre-present in the setup in which it executes. .NET Core 3.0 comes with support known as self-contained executables, which enable you to publish applications as a single executable for a given supported platform such as Linux, Windows, 32-bit, or 64-bit.
In order to simulate the creation of a self-contained app, I will use the .NET 5 SDK. To see the version of dotnet SDK, we run the following command:
C:\Users\Habib\source\repos\Adopting-.NET-5--Architecture-Migration-Best-Practices-and-New-Features\code\Chapter01>dotnet –version 5.0.100-rc.2.20479.15
For demonstration purposes, I will create a sample console app via the command line:
C:\Users\Habib\source\repos\Adopting-.NET-5--Architecture-Migration-Best-Practices-and-New-Features\code\Chapter01>dotnet new console -o myscapp
Then, the command to generate the self-contained executable (for Linux 64-bit) would look like this:
C:\Users\Habib\source\repos\Adopting-.NET-5--Architecture-Migration-Best-Practices-and-New-Features\code\Chapter01>dotnet publish -c release --self-contained --runtime linux-x64 -o c:\apps\myscapp_lx
Executing dir C:\apps\myscapp_lx
shows me something in terms of which end part is something like this, with 186 files and around 75 MB of disk space:
...25/09/2020 23:06 16.776 WindowsBase.dll 186 File(s) 75.306.803 bytes 2 Dir(s) 709.082.009.600 bytes free
In order to generate a self-contained executable in a single .exe
file (for a Windows 64-bit platform), the command would be as follows:
C:\Users\Habib\source\repos\Adopting-.NET-5--Architecture-Migration-Best-Practices-and-New-Features\code\Chapter01\myscapp>dotnet publish -c release --self-contained --runtime win-x64 /p:PublishSingleFile=true -o c:\apps\myscapp_win Microsoft (R) Build Engine version 16.8.0-preview-20475-05+aed5e7ed0 for .NET Copyright (C) Microsoft Corporation. All rights reserved. Determining projects to restore... Restored C:\Users\Habib\source\repos\Adopting-.NET-5--Architecture-Migration-Best-Practices-and-New-Features\code\Chapter01\myscapp\myscapp.csproj (in 36,23 sec). You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview myscapp -> C:\Users\Habib\source\repos\Adopting-.NET-5--Architecture-Migration-Best-Practices-and-New-Features\code\Chapter01\myscapp\bin\release\net5.0\win-x64\myscapp.dll myscapp -> c:\apps\myscapp_win\
I have the following output after running this command with the .NET 5 SDK:
This generated six files instead of 180 plus files, and the main .exe
file is around 50 MB on my machine using the .NET 5 RC2 SDK, and this file contains within itself all the required libraries from the framework and the runtime.
Additionally, .NET also provides a feature to generate the trimmed version of the self-contained executables. What this means is that the output will only contain the required assemblies that are used by the application code and avoid including any other runtime libraries that are not used. If the code is using reflection, the .NET assembly linker does not know about this dynamic behavior and therefore it could not include those types and the libraries that are required and expected by the code that uses the reflection at runtime. Therefore, you need to indicate the linker in relation to any of the types needed by reflection in the specified libraries.
A simple command line to generate the trimmed self-contained version would look like this:
C:\Users\Habib\source\repos\Adopting-.NET-5--Architecture-Migration-Best-Practices-and-New-Features\code\Chapter01\myscapp>dotnet publish -c release --self-contained --runtime win-x64 /p:PublishSingleFile=true /p:PublishTrimmed=true -o c:\apps\myscapp_win_2t
Running this command will generate the following output:
Comparing the generated output files with our previous command line without using the trim option, which generated around 50 MB, this one generated around 11 MB of .exe
files, and this contained all of the required .NET 5 runtime and framework libraries!
While single file and trimming might be regarded as a cosmetic change, the self-contained option is really a safeguarding boundary for your application so that it can run safely and in a stable manner with other applications using any other .NET runtime, especially when your app is not running in the container but in a shared environment.
.NET 5 app trimming optimizations
In .NET 5, trimming has been further optimized where the assembly linker goes deep inside the assemblies and removes even the types and members that are not used in all of the referenced assemblies. This is known as member-level trimming.
Member-level trimming can be enabled by passing -p:PublishTrimmed=True
and -p:TrimMode=Link
to the dotnet publish
command.
In .NET 5, in order to give hints to the trimmer (known as the ILLink) for dynamic code such as reflection, a set of attributes has been added that enables the code to be annotated, and so should be included by the ILLink.
One more feature that is being added to .NET 5 allows us to conditionally remove code from applications using the feature switches. Feature switches remove the features provided by the framework, which even reduces the size of the .NET runtime.
Tip
Remember, when you trim your app, that it is necessary to perform exhaustive end-to-end testing of the published version of the application. The testing of a trimmed app should be carried out externally rather than by unit tests or code within the app, as the published code is only the trimmed output.
ReadyToRun
Another feature that has been introduced, starting with .NET Core 3.0 and above, is ReadyToRun (R2R). R2R is a kind of ahead-of-time (AOT) compilation. It improves the startup time of your application by natively compiling your application assemblies in R2R format at build time so that JIT is not required to be done when assemblies are executed the first time.
R2R has the most significant effect in terms of startup time. Later, there should be little difference once the process is already warmed up.
Developing programs for .NET
To develop .NET programs, a fully fledged IDE is not required. The CLI tools provided, specifically the dotnet command, are enough to compile and build the programs. You can also use free, open source, and cross-platform Visual Studio Code, or Express, which is a free version of Visual Studio. For .NET Core 3 and .NET 5, you will at least require Visual Studio 2019.
You can also use Visual Studio Code Remote Development, which enables you to use a container, VM, remote machine, or the Windows Subsystem for Linux (WSL) as a fully fledged development environment. This allows your development machine to be different to your deployment machine. Your remote host (container, VM), which is used for building the application, is fully configured and streamlined according to the app requirements, and hence you would no longer want to modify your development machine. Binaries resulting from the source code are not even required on your local machine, which is why you can perform remote debugging on your remote host.
From May 2019, Microsoft introduced Visual Studio Codespaces, which provides Azure-powered, managed, on-demand development environments. You can work in these environments, which differ from your development machines, via Visual Studio Code or Visual Studio 2019, with certain extensions installed in your IDE.
You would need to use codespaces in these situations, where you may like to try out new tech, a new tool, or a different technology stack, without disrupting your own primary development environment, or setting these development environments locally would simply take too much time before you can even try it out. Unless we are ready to brick our fine-tuned development machine, we would mostly hesitate trying out a new stack, such as Node.js, Android development, or Python. This is where codespaces come in.
Head on over to the following link for more information:
https://code.visualstudio.com/blogs/2019/05/02/remote-development.
Next, we will be understanding performance improvements and checking how does it work in different .NET versions.