Exciting New Features in .NET 5

Subscribe to my newsletter and never miss my upcoming articles

.NET 5.0 was officially released this week, bringing with it a range of improvements to the .NET ecosystem. Like many .NET developers, I was quick to download it and give it a test run. This article discusses some of the most exciting new features in .NET 5.

What is .NET 5?

.NET is Microsoft's developer platform, used to write applications for a range of different applications, including web, mobile and desktop applications. .NET is the predominant platform for developers that write in the C#, F# and VB languages.

Originally the .NET Framework was designed to run only on Windows machines, giving it significant limitations. That all changed in 2016, when the first version of .NET Core was released, allowing developers to write cross-platform applications for almost any type of device.

However, both the .NET Framework and .NET Core continued to be developed simultaneously. Until now.

.NET 5 represents the unification to the two separate frameworks, bringing together the best features of both into one single framework. Going forward, Microsoft will only continue to update this new unified framework, starting with .NET 5.

Best New .NET 5 Features

C# 9

.NET 5 brings along with it updates to the C# (C# 9) and F# (F# 5) languages. I apologise to F# developers that I don't have experience in F# so will be leaving it out of this review.

Top-level statements

Anyone the has written any C# code would admit that there can be quite a lot of boilerplate code, even to make a very simple application, which can be unnecessary and confusing. For example, the most basic Hello World program would look like this:

using System;

namespace MyConsoleApp
{
    class Program 
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World");
        }
    }
}

Top-level statements makes some assumptions about your code, that allow you to simplify it. For example, if no namespace is declared, it is assumed that the global namespace is being used. Also, if not specified, it is assumed that the code belongs in the Main method of the Program class. Therefore, using top level statements, the same program can be simplified to:

using System;

Console.WriteLine("Hello World");

In my opinion, the intent is much clearer here, with all of the boilerplate removed. To learn more about top-level statements, this article is very useful.

Record types

A pattern that is becoming more and more popular in programming is that of immutability. This is the idea that, once an object is created, it cannot be changed (or mutated). This helps make more reliable code, since the potential for unexpected side effects is reduced.

C# 9 introduces Record types, which act as immutable complex reference types. So, for example., we could define a Person record:

public record Person
{
    public Person(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastName;
    }

    public string FirstName { get; }
    public string LastName { get; }
}

The values of record type properties cannot be changed once the object has been initialised.

Another nice feature of record types is that they can be compared in the same way that value types are. That is, if the values within the record are equal, then the records themselves are considered equal. This is different to normal reference types, which determine equality based on if they refer to the same instance. For example:

// if Person is a class
var sam1 = new Person("Sam", "Walpole");
var sam2 = new Person("Sam", "Walpole");
sam1 == sam2 // false

// if Person is a record
var sam1 = new Person("Sam", "Walpole");
var sam2 = new Person("Sam", "Walpole");
sam1 == sam2 // true

There are some other nice features of Record types, which can be explored further here.

Init-only setters

It is common to initialise classes in the following way:

var person = new Person
{
    FirstName = "Sam",
    LastName = "Walpole"
};

However, you may have experienced cases where you want to have no setter or a private setter for a particular property, if you do not want it's value to be changed after initialisation. The problem with this is the above example no longer works as it relies on having a public setter available. This means that you have to resort to including all initialisable properties in the constructor of you class, which for complex classes can become long and difficult to understand easily.

C# 9 introduces init-only setters, which allows the above syntax to be used for initialisation, but afterwards the property does not have a setter and is therefore unmodifiable.

public class Person
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
}

.NET MAUI

.NET MAUI stands for .NET Multi-platform App UI and is a user interface framework that allows the same code to be reused to deploy Android, iOS, macOS and Windows, all in one project.

.NET MAUI supports the well-know MVVM (Model View ViewModel) pattern, as well as a new MVU (Model View Update Pattern). To learn more about .NET MAUI, please check here.

Entity Framework Core 5.0

While not technically officially part of .NET 5, EF Core is the go-to ORM for many .NET developers, and it's release cycle coincides with that of .NET 5.

There are a whole bunch of updates coming to EF Core 5, all of which can be found here. However, below is the update that I'm most excited about.

Many-to-many relationship mapping

This has been a big frustration for users of EF Core for a long time now. In previous versions of EF Core, in order to configure a many-to-many relationship, it was necessary to explicitly create a class for the joining table, that had no purpose other than holding the foreign keys.

public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Text { get; set; {

    public ICollection<PostTag> PostTags { get; set; }
}

public class Tag
{
    public int Id { get; set; }
    public string Name { get; set; }

    public ICollection<PostTag> PostTags { get; set; }
}

public class PostTag
{
    public int Id { get; set; }

    public int PostId { get; set; }
    public Post Post { get; set; }

    public int TagId { get; set; }
    public Tag Tag { get; set; }
}

As you can see, this is an awful lot of additional boilerplate code, which can be very frustrating when you have lots of many-to-many relationships to deal with.

Luckily, in EF Core 5, many-to-many relationships can be configured simply by having a collection property on both entities, eliminating the need for that joining class.

public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Text { get; set; {

    public ICollection<Tag> Tags { get; set; }
}

public class Tag
{
    public int Id { get; set; }
    public string Name { get; set; }

    public ICollection<Post> Posts { get; set; }
}

Conclusion

Above I have discussed some of the new features in .NET 5 that I am most excited about. .NET 5 brings with it a whole host of other big changes, so no doubt there are features that I've missed. What are your favourite features coming up in .NET 5? Let me know in the comments below!

I post mostly about full stack .NET and Vue web development. To make sure that you don't miss out on any posts, please follow this blog and subscribe to my newsletter. If you found this post helpful, please like it and share it. You can also find me on Twitter.

No Comments Yet