Heads up! .NET 6 (and ASP.NET 6, by extension) is set to be released at the 2021 .NET Conf, between November 9 and 11. If you’re thinking “wasn’t .NET 5 just released” or “I just started with .NET 3.1,” well, you’re not wrong—.NET 5 was released on November 10, 2020, and .NET 3.1 was waaay back in December 2019. Microsoft has committed to yearly releases for .NET going forward, so they are right on schedule with .NET 6.

Bundled in this new release cadence is a new support policy: From .NET 5 on, odd-numbered version releases are patched for 18 months, while even-numbered release versions are patched for three years. If you already migrated your apps to .NET 3.1, note that it has a three-year patch cycle that ends in December 2022; if you are still on any prior version of .NET Core, you are currently out of the support cycle. While no official end of support has been announced for .NET Framework 4.6.2 and later, Microsoft has stated that .NET Framework 4.8 was the last major release of the .NET Framework; new development should target the platform formerly known as .NET Core (e.g., .NET 6). The writing is on the wall for the full framework—start making plans to upgrade!

Staying within patch support is important, but .NET 6 brings a lot of performance improvements and productivity gains along with it. We’ve seen some impressive gains in execution speed and memory usage in each successive release of .NET. If you haven’t been keeping track, chances are you will be blown away by the cumulative gains over .NET Framework or even earlier versions of .NET Core.

Put all of that together and you start to see that .NET 6 is a very important release for the .NET ecosystem.

Changes Aplenty

Where .NET Core 3.1 was about finalizing the evolution of the .NET Framework to be cross-platform, .NET 5 began to look ahead and refine that vision. As a long-term support (LTS) version, .NET 6 represents a further refinement, with a ton of design and performance improvements. We’ll primarily focus on ASP.NET 6 here, but have a look at the list of performance improvements in the runtime and breaking changes in .NET 6 to see just how much ground has been covered.

C# Language Updates

 The C# language itself, now v10.0, has a handful of interesting changes. For fans of brevity, global using directives and file-scoped namespace directives are nice additions. Now, you can declare global usings that apply to the entire compilation unit (most likely a project) and avoid needing that same set of using directives you always add at the top of every file. File-scoped namespaces also allow you to declare the namespace that applies to all code in a given file with a single line—no more matching curly braces required, and one less indent level in your source files.

BEFORE

CSharp9_Widget.cs

using System;
using System.Collections.Generic;
using System.Linq;

namespace MyNamespace.Data
{
	class Widget
	{
		private List<WidgetPart> _widgetParts;
		public IEnumerable<WidgetPart> RequiredParts => 
			_widgetParts;

		public bool RequiresPart(int partId) => 
			_widgetParts.Any(x => x.Id == partId);
	}
}

CSharp9_WidgetPartsInventory.cs

using System;
using System.Collections.Generic;
using System.Linq;

namespace MyNamespace.Data
{
	class WidgetPartsInventory
	{
		private Dictionary<int, WidgetPart> _widgetPartsById;

		public bool HavePartsToBuildWidget(Widget widget) => 
			widget.RequiredParts.All(p => 
				_widgetPartsById.ContainsKey(p.Id));
	}
}

AFTER:

CSharp10_GlobalUsing.cs

global using System;
global using System.Collections.Generic;
global using System.Linq;

CSharp10_Widget.cs

namespace MyNamespace.Data;

class Widget
{
	private List<WidgetPart> _widgetParts;
	public IEnumerable<WidgetPart> RequiredParts => _widgetParts;

	public bool RequiresPart(int partId) => 
		_widgetParts.Any(x => x.Id == partId);
}

CSharp10_WidgetPartsInventory.cs

namespace MyNamespace.Data;

class WidgetPartsInventory
{
	private Dictionary<int, WidgetPart> _widgetPartsById;

	public bool HavePartsToBuildWidget(Widget widget) => 
		widget.RequiredParts.All(p => 
			_widgetPartsById.ContainsKey(p.Id));
}

There are a few other updates relating to records, patterns, and interpolated strings, but these are mostly syntactic sugar.

ASP.NET Core Updates

There have been seven preview releases of ASP.NET Core 6 so far. Read the release notes for each and it’s easy to see that ASP.NET is a core focus (see what I did there?). There’s lots to love here.

Web Host and Minimal API

Since the inception of ASP.NET Core, each application has split the app initialization code between two separate classes—typically contained in Program.cs (for creating the web host) and Startup.cs (for configuring application concerns like routing and IoC container configuration). The Startup class in particular had a sort of magical feel since its methods were never called directly by the developer. Rather, the webhost called configuration methods automatically behind the scenes.

The design team analyzed this setup compared to other web frameworks and decided that setup involved too much ceremony. Thus the Minimal API concept was born. See here for a lot more technical background on the previous designs compared to the new changes introduced in ASP.NET Core 6.

Now, application initialization can all be contained within one file (if desired). It feels strange, but a Main method is no longer required, either. Routes can be defined in the app setup, leading to a greatly reduced amount of code to get a basic application up and running.

AspNetMinimalApi.txt

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
 
app.MapGet("/", () => "Hello World!");
 
app.Run();

Not bad for four lines of code!  A more realistic web app using Razor Pages is still neat and compact:

AspNetMinimalApiRazor.txt

var builder = WebApplication.CreateBuilder(args);
 
builder.Services.AddControllersWithViews();
 
var app = builder.Build();
 
if (app.Environment.IsDevelopment())
{
		app.UseDeveloperExceptionPage();
}
else
{
		app.UseExceptionHandler("/Home/Error");
		app.UseHsts();
}
 
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
 
app.Run();

If you happen to still like the organizational style of separating the setup of your service from your app configuration, you can still create extension methods for IServiceCollection and IApplicationBuilder and call them from the builder and app objects, respectively. Bottom line, you have a bit more control over your code organization now, and the experience is simplified.

Build Time Improvements

The Razor compiler (used to build views for web apps into app code) has gotten a drastic speed improvement. Microsoft updated the compiler to use Roslyn Source Generators. The end result is less waiting at build time so your debug sessions will start up much faster. Microsoft’s benchmarks show build times decreased in the neighborhood of 50% for their default app templates, and one would expect that to really add up for projects of significant size.

Hot Reload

Hot reloads have been possible with many Javascript frameworks for several years, and it’s now becoming a reality for ASP.NET Core apps in C#:  With Hot Reload, you can edit your C# code while your app is running (under the debugger), and your code changes will automatically be reflected in your app without losing app state. In other words, the app doesn’t have to restart; only the specific code module you edited gets reloaded. This should be a nice improvement for debugging and interactive development workflows.

You’ll need to update Visual Studio to enable the feature there, and it is available in VS 2019 16.11 and later. Note also that edits to Razor pages are not supported at this time, but that’s in the works.

Blazor

Blazor is also getting a lot of love in ASP.NET Core 6. For instance, Blazor apps can now be compiled directly to WebAssembly for a nice application speed boost over IL interpreted (i.e .NET native compiled) versions of the same code. The trade-off so far is that the compilation time can be lengthy—several minutes or more—and the resulting application binary is around 2x larger. Local compile / debug experience is still fast as the long compilation times only apply for packaging/release.

Speaking of performance, Blazor WebAssembly enables multithreading in client code. Javascript is constrained to a single thread in the browser. Real multithreading opens up some new possibilities for apps that could benefit from parallel processing (subject to support by the browser, of course).

There’s also a very interesting move to make Blazor viable for targeting desktop applications via MAUI. The big promise of Blazor is that developers can write web applications fully in C# with no need to swap over to Javascript for the front end. Without that extra seam between C# and Javascript, there’s no need for a mapping layer between front-end and back-end code. The same C# models can be used on both sides, meaning less code is required and therefore less time is required to develop an application. Blazor desktop further extends this concept to allow this shared code to now also be seamlessly integrated with desktop applications. Our team has previously looked at Blazor and found it to have a compelling developer story, though worth further evaluation before full adoption. This release appears to fill some of the previous gaps that prevented us from explicitly recommending it for teams.

MAUI

Short for Multi-platform App UI, MAUI is Microsoft’s next take on a cross-platform UI framework. MAUI is an evolution of Xamarin to also include desktop platforms. It allows targeting of iOS, Android, macOS, and Windows from a single codebase. MAUI handles the abstractions to native platform APIs so you get access to things like device sensors in a platform-neutral way. One criticism of Xamarin (and cross-platform UIs in general) is that they end up with least-common-denominator interfaces that never quite look good on any platform. It remains to be seen just how well MAUI will address this concern. If you’re less concerned about presentation than you are with development speed and maintenance costs across multiple platforms, MAUI is worth a closer look. As always with UI frameworks from Microsoft, we recommend a wait-and-see approach to understand where the platform is headed for the long term before full adoption.

Final Thoughts

.NET 6 feels like the biggest release the ecosystem has seen since the .NET 4 Framework. While .NET Core was a significant strategic move several years ago, .NET 6 has a huge head of steam now. What we covered above are the most noteworthy changes, but there’s much more to .NET 6.

For so long, the mantra from Redmond has been “developers, developers, developers,” and .NET 6 certainly delivers on that. There are individual productivity improvements like the Minimal API and Hot Reload, and then there are changes that will also accumulate at the team level, like unified development on Blazor. As a software consultancy, our mantra has always been “Win with speed”, and we’re happy to see Microsoft providing the tools to do so.

Contact an expert