March 11th, 2021

Announcing .NET 6 Preview 2

Richard Lander
Product Manager

Today, we are glad to release .NET 6 Preview 2. It includes new APIs, runtime performance improvements, and early builds of .NET MAUI. It also includes builds for Apple Silicon, which were missing for Preview 1. After the announcement of the overall .NET 6 release, we’re now back to regularly scheduled monthly previews until the final release in November. You will see new features included in each preview that deliver on .NET 6 themes, epics and user stories. These themes offer improvements to every .NET application, including for server/cloud, desktop, IoT and mobile.

You can download .NET 6 Preview 2, for Windows, macOS, and Linux.

See the ASP.NET Core and EF Core posts for more detail on what’s new for web and data access scenarios.

.NET 6 has been tested with Visual Studio 16.9 and Visual Studio for Mac 8.9. We recommend you use those builds if you want to try .NET 6.

In this post, two of my team members will cover the .NET 6 themes that they are working on, specifically Improve inner-loop performance for .NET developers and .NET has a great client app development experience. I covered Improve startup and throughput using runtime execution information (PGO) in the .NET 6 Preview 1 post.

Support

.NET 6 will be released in November 2021 and will be supported for three years, as a Long Term Support (LTS) release. The platform matrix has been significantly expanded compared to .NET 5.

The additions are:

  • Android.
  • iOS.
  • Mac and Mac Catalyst, for x64 and Apple Silicon (AKA “M1”).
  • Windows Arm64 (specifically Windows Desktop).

.NET 6 Debian container images are based on Debian 11 (“bullseye”), which is currently in testing.

Theme — Improve .NET Inner Loop Performance

Stephen Toub is the author of this theme section. He’s part of the team working on the inner loop theme, and is sharing his team’s goals and plans.

Performance is top of mind for all .NET releases. Over the past several versions, a lot of effort has gone into improving throughput, reducing memory consumption, and other factors that impact the steady-state performance of an application. For .NET 6, we’re also focused on a few additional aspects, several of which are highlighted as part of one of the .NET 6 themes: improving inner-loop performance for .NET developers. Just as we’ve focused on ensuring that apps and services have the best possible performance, we also want to ensure that developers are able to be as productive as possible, optimizing the tools and workflows that are frequently used as part of their “inner loop” (meaning the processes they use repeatedly as part of making code changes, building, and testing).

Some of these efforts look very similar to traditional throughput efforts of the past, but instead of focusing on steady-state performance, they’re focused on startup performance, of the runtimes, the app models, the dotnet CLI, MSBuild, etc., as well as the end-to-end performance of the tools (especially for smaller solutions, as historically they haven’t received as much attention). The mindset involved in such optimizations is often quite different from the mindset used when optimizing for steady-state throughput. For steady-state work, you might focus on caching values that could be reused in the future, but often for startup performance you’re focused on operations that may only be invoked once and where the costs for that first invocation matter. The work involved here does, however, look like many other performance efforts, with a typical profile-analyze-fix loop: you profile the relevant area of the application you want to optimize, you analyze the resulting data to look for the top offenders and bottlenecks, you come up with fixes for them, and then you start the process over to find the next impactful items. We’re still early in the .NET 6 development cycle, but we’ve already been successful in trimming overheads from key areas involved in developers’ inner loops, focusing on various dotnet commands like new, build, and run. Example improvements thus far include fixing places tools were unexpectedly JIT’ing (dotnet/installer #9635), avoiding expensive logging-related work happening even when logging wasn’t enabled (dotnet/aspnetcore #27956), optimizing globbing in MSBuild (dotnet/msbuild #6151), changing the ASP.NET Razor compiler to use a Roslyn source generator in order to avoid an extra compilation process (https://github.com/dotnet/sdk/pull/15756), and changing the .NET host to use file access patterns less likely to trigger antivirus tooling on the machine (dotnet/runtime #48774).

Of course, one of the best performance optimizations is one that avoids the need for work to be done entirely, and that is the focus of the other half of this .NET 6 theme: .NET hot reload. Hot reload will improve developer productivity, across all supported operating systems and hardware platforms, by enabling code to be edited while an app is running, even without a debugger attached. With many kinds of edits, no restart will be required: the developer will save their changes, and the new version of the code will be applied to the executing process. Upon wanting to make a change to the app or service, rather than needing to stop it and go through the typical inner loop cycle of making the change, building, running, and getting back to the point in the app or service that triggered the need for the change in the first place, that whole cycle can be skipped for many kinds of edits. The work involved in enabling this spans the .NET release, requiring investments in the runtimes (both coreclr and mono), the C# Roslyn compiler, the app models (for example, Blazor, .NET MAUI), and the developer tools (for example, CLI, Visual Studio), but it is expected to fundamentally improve the way .NET developers go about writing their apps and services.

Image dotnet6 build improvements

The chart above demonstrates some early results of the inner loop effort.

Theme: .NET has a great client app development experience

Maddy Leger is the author of this theme section. She is part of the team working on the client app development theme, and is sharing her team’s goals and plans.

One of the most exciting parts of .NET 6 is mobile development, which is currently offered as the separate Xamarin product. Over time, we’ve been making Xamarin more similar to mainline .NET. It’s now time to deliver a fully unified mobile product for .NET. With .NET 6, iOS, Android, and macOS development will be integrated into the .NET SDK experience and use the .NET libraries. For the past couple years, we’ve been working to fold Mono into .NET, so developers can leverage the strengths of both runtimes without having to target different .NET versions or worry about compatibility issues. In .NET 5, we moved Blazor WebAssembly over, and are using that same model for Xamarin. .NET 6 is the culmination of this unification effort, encompassing a key Epic of the theme – Xamarin developers can upgrade to and use the latest .NET SDKs for their existing apps.

Now that all your .NET apps will run on the same libraries, we want to increase the amount of code you share across desktop and mobile platforms. Xamarin.Forms, Xamarin’s cross-platform UI framework, is evolving into .NET Multi-platform App UI, enabling you to easily write apps for iOS, Android, Windows, and macOS with the same codebase. .NET MAUI is shipping as part of .NET 6 along with a bunch of performance and tooling improvements like .NET/C# Hot Reload, more shared resources and code across different platforms, and better page rendering performance with a more flexible set of UI controls. You can follow the overall Epic: Xamarin/.NET MAUI developers have improved app performance and share more code with .NET 6.

.NET MAUI isn’t just for client app developers. Thanks to the refactored control set and the ability to run on the .NET 6 libraries, your existing Blazor apps will run natively on Windows and macOS via .NET MAUI. You’ll be able to seamlessly weave in native controls and functionality alongside your Blazor codebase, including platform specific functionality. See the Preview 1 blog for some screenshots of “Blazor desktop” in action.

The final epic we’re focused on in this theme is about packaging, deploying, and releasing your cross-platform client apps. Because there are so many developers/target platforms/ways to develop your apps, there are a lot of different app packages you have to distribute at the end of the day. Especially with Blazor desktop, we want to make that experience as seamless as possible. We’re looking at strategies for improving release and versioning both locally and in the cloud as part of the epic Desktop developers can package, distribute, release, and update their applications to multiple desktop platforms and architectures.

To summarize, in .NET 6 you’ll be able to:

  • Build iOS, Android, and macOS apps using the .NET libraries.
  • Create iOS, Android, Windows, and macOS apps from the same codebase using .NET MAUI.
  • Share more code and resources across platforms (such as images, app icons/manifests, and more).
  • Run your Blazor web apps natively on the macOS and Windows.
  • Easily package and distribute your apps for all the target frameworks you include.

We’ve already made a lot of progress and will continue to add more features into each .NET 6 Preview. For more info, you can follow the .NET MAUI repo, which is kept up to date with progress reports.

The remainder of the post is dedicated to features that are new in Preview 2.

.NET Multi-platform App UI

We have added .NET MAUI and single project developer experiences for Android, iOS, and Mac Catalyst. We have updated our samples repository with projects you can run today with .NET 6 Preview 2. As we progress through previews, more and more functionality will be enabled. Today your best experience running these projects is via the command line.

Mac Catalyst

You can add the following TargetFramework settings to your project to build for macOS desktop, using Mac Catalyst.

<TargetFrameworks>net6.0-android;net6.0-ios</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">$(TargetFrameworks);net6.0-maccatalyst</TargetFrameworks>

A single, multi-targeted application project

We’ve now enabled a single project experience for .NET MAUI apps. You now get a very clean solution that can run on Android, iOS, and macOS. Windows support depends on WinUI 3 which is in its own preview now. We will add Windows to .NET MAUI in a future preview. See the roadmap for more details.

To use this today, follow the guidance in our samples repository to build and run from the command line. Future versions of Visual Studio will add device selections and run options.

single, multi-targeted application project

Shared fonts, images, and app icons

Fonts and images can be placed in one location in your solution and .NET MAUI will enable them to natively work on all platforms you target. These are tracked in your *.csproj as SharedImage and SharedFont.

<ItemGroup>
    <SharedImage Include="appicon.svg" ForegroundFile="appiconfg.svg" IsAppIcon="true" />
    <SharedFont Include="Resources\Fonts\ionicons.ttf" />
  </ItemGroup>

Both accept wildcards to include all files within a location.

<ItemGroup>
    <SharedImage Include="appicon.svg" ForegroundFile="appiconfg.svg" IsAppIcon="true" />
    <SharedImage Include="Resources\Images*" />
    <SharedFont Include="Resources\Fonts*" />
  </ItemGroup>

MauiApp with Host Builder for bootstrapping your app

We have extensions for configuring services, fonts, and compatibility renderers for migrating Xamarin.Forms projects. IWindow has been introduced for multi-window support coming in a future release. This new pattern also introduces a single, consistent place for library authors and control vendors to integrate with .NET MAUI.

public class Application : MauiApp
{
    public override IAppHostBuilder CreateBuilder() => 
        base.CreateBuilder()
            .RegisterCompatibilityRenderers()
            .ConfigureServices((ctx, services) =>
            {
                services.AddTransient<MainPage>();
                services.AddTransient<IWindow, MainWindow>();
            })
            .ConfigureFonts((hostingContext, fonts) =>
            {
                fonts.AddFont("ionicons.ttf", "IonIcons");
            });

    public override IWindow CreateWindow(IActivationState state)
    {
        Microsoft.Maui.Controls.Compatibility.Forms.Init(state);
        return Services.GetService<IWindow>();
    }
}

New Control Handlers

We have introduced the first controls and properties that implement a new handler approach. These include partial implementations of Button, Label, and Entry, Slider, and Switch. We are now accepting pull requests to fill out complete implementations, and several contributors have already successfully contributed. For more information on joining the effort, visit the handler property guidelines.

The HelloMaui sample application now runs from a single project to macOS, iOS, and Android showcasing the current set of ported controls.

macOS: Screen Shot 2021-03-08 at 10 57 44 AM

iOS: Screen Shot 2021-03-08 at 2 39 34 PM

Android: Android emulator screenshot of .NET MAUI controls

Updates to Mobile SDKs

The mobile SDKs and surrounding tooling are gaining capabilities in this release as well. These capabilities will continue to improve as they are integrated with future Visual Studio releases.

Android:

  • Android X libraries are now available for .NET 6 and the default dependency for Android apps

iOS:

  • Developers on Windows can use the Remote iOS Simulator
  • Developers on Windows can connect to the remote Mac build host
  • Ahead-of-time compilation has been added to enables building and deploying to physical iOS hardware

.NET Libraries

The following APIs and improvements have been added to the .NET libraries.

System.Text.Json – ReferenceHandler.IgnoreCycles

JsonSerializer (System.Text.Json) now supports the ability to ignore cycles when serializing an object graph. The ReferenceHandler.IgnoreCycles option has similar behavior as Newtonsoft.Json ReferenceLoopHandling.Ignore. One key difference is that the System.Text.Json implementation replaces reference loops with the null JSON token instead of ignoring the object reference.

You can see the behavior of ReferenceHandler.IgnoreCycles in the following example. In this case, the Next property is serialized as null since it otherwise creates a cycle.

class Node
{
    public string Description { get; set; }
    public object Next { get; set; }
}

void Test()
{
    var node = new Node { Description = "Node 1" };
    node.Next = node;

    var opts = new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.IgnoreCycles };

    string json = JsonSerializer.Serialize(node, opts);
    Console.WriteLine(json); // Prints {"Description":"Node 1","Next":null}
}

PriorityQueue

PriorityQueue<TElement, TPriority> (System.Collections.Generic) is a new collection that enables adding new items with a value and a priority. On dequeue the PriorityQueue returns the element with the lowest priority value. You can think of this new collection as similar to Queue<T> but that each enqueued element has a priority value that affects the behavior of dequeue.

The following sample demonstrates the behavior of PriorityQueue<string, int>.

// creates a priority queue of strings with integer priorities
var pq = new PriorityQueue<string, int>();

// enqueue elements with associated priorities
pq.Enqueue("A", 3);
pq.Enqueue("B", 1);
pq.Enqueue("C", 2);
pq.Enqueue("D", 3);

pq.Dequeue(); // returns "B"
pq.Dequeue(); // returns "C"
pq.Dequeue(); // either "A" or "D", stability is not guaranteed.

Credit to community member Patryk Golebiowski for contributing the implementation.

Better parsing of standard numeric formats

We’ve improved the parser for the standard numeric types, specifically for .ToString and .TryFormat. They will provide better results when precision > 99 decimal places is specified. Also, the parser now better supports trailing zeros in the Parse method.

The following examples demonstrate before and after behavior.

  • 32.ToString("C100") -> C132
    • .NET 6: $32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
    • .NET 5: We had an artificial limitation in the formatting code to only handle a precision of <= 99. For precision >= 100, we instead interpreted the input as a custom format.
  • 32.ToString("H99") -> throw a FormatException
    • .NET 6: throws a FormatException
    • This is correct behavior, but it’s called here to contrast with the next example.
  • 32.ToString("H100") -> H132
    • .NET 6: throw a FormatException
    • .NET 5: H is an invalid format specifier. So, we should’ve thrown a FormatException. Instead, our incorrect behavior of interpreting precision >= 100 as custom formats meant we returned wrong values.
  • double.Parse("9007199254740997.0") -> 9007199254740998
    • .NET 6: 9007199254740996.
    • .NET 5: 9007199254740997.0 is not exactly representable in the IEEE 754 format. With our current rounding scheme, the correct return value should have been 9007199254740996. However, the last .0 portion of the input was forcing the parser to incorrectly round the result and return 9007199254740998.

Changes:

  • https://github.com/dotnet/runtime/issues/46827
  • https://github.com/dotnet/docs/pull/23046
  • https://github.com/dotnet/docs/issues/22458

SignalR – Nullable annotations

The ASP.NET Core SignalR Client package has been annotated for nullability. This means that the C# compiler will provide appropriate feedback, when you enable nullability, based on your handling of nulls in SignalR APIs. The SignalR server has already been updated for nullability in 5.0, but a few modifications were made in 6.0. You can track ASP.NET Core support for nullable annotations at dotnet/aspnetcore #27389.

Nullable annotations were a big focus of the .NET Core 3.x and .NET 5 releases. All of the .NET libraries (AKA “base class libraries”) were annotated as part of those releases. The System.Device.Gpio package was also annotated as part of the .NET 5 release.

You need to add <Nullable>enable</Nullable> in your project file to use the nullable annotations.

Runtime

The following improvements have been made in (or related to) the .NET runtime.

Framework Assemblies are compiled with Crossgen2

Update: This change (compiling more assemblies with crossgen2) didn’t make it into Preview 2, but will be part of Preview 3.

All of the .NET libraries are now compiled with crossgen 2, across all supported operating systems and architectures. This includes all libraries in the Microsoft.NETCore.App directory, but not the other frameworks, such as ASP.NET or Windows Desktop. Those frameworks will be transitioned to crossgen 2 in Previews 3 and/or 4.

Crossgen 2 itself isn’t intended to improve performance. As I said in the preview 1 post, the purpose of Crossgen 2 is to enable new performance features like PGO. That said, Crossgen 2 has delivered modest size-on-disk improvements based on some targeted size optimizations, as you can see in the following comparison. Main point is that moving to Crossgen 2 does not incur any up-front regressions. It is effectively neutral, as promised.

Size [MB] FullName
--------- --------
64.22     C:Program FilesdotnetsharedMicrosoft.NETCore.App5.0.3
63.31     C:Program FilesdotnetsharedMicrosoft.NETCore.App6.0.0-preview.1.21102.12
63.00     C:Program FilesdotnetsharedMicrosoft.NETCore.App6.0.0-preview.2.21118.6

Profile guided optimization

Profile guided optimization enables us to generate code that is optimal based on a variety of characteristics. We are building both static and dynamic PGO variations.

The following improvements have been made in Preview 2:

JIT improvements

The following improvements were made to optimize the code generated by the JIT.

The first improvement is the latest result of a project to stabilize performance measurements. You can read recent analyses (January 26, 2021; February 3, 2021) that provide an in-depth explanation of our progress.

The second improvement is specific to Arm64. We continue to improve the performance of the code that the JIT generates for Arm64, in collaboration with Arm engineers.

Closing

We’re back to the regular monthly previews, with .NET 6. Please give .NET 6 Preview 2 a spin in your environment, in a container, a virtual machine or on a test machine. We want your feedback to help us improve these new features before we release the final version in November. We expect that the majority of the release should be done by July; we’ll focus on quality after that. That gives you a sense of the windows we have for feedback. The earlier you can give feedback, the better.

You can expect to see more in .NET 6 for mobile and Blazor Desktop in subsequent previews. We’re still in the process of building those features.

Author

Richard Lander
Product Manager

Richard Lander is a Program Manager on the .NET team. He works on making .NET work great in memory-limited Docker containers, on Arm hardware like the Raspberry Pi, and enabling GPIO programming and IoT scenarios. He is part of the design team that defines new .NET runtime capabilities and features. Favourite fantasy: Dune and Doctor Who. He grew up in Canada and New Zealand.

54 comments

Discussion is closed. Login to edit/delete existing comments.

  • Jon Miller

    I think they should have called .NET MAUI, Silverlight++ instead.

  • Masoud MJ

    Please just implement coroutines like Golang’s goroutines

  • Trevor Hall

    Hi Richard,
    There is something I've been looking for for a long time in .NET that never seems to be on the way:
    atomic seek and read.
    This has been available on the Windows platform for a very long time with "Overlapped IO".
    It has been available for some time in Linux with pread.

    With random access files, it is possible to call Seek and then Read, but they are separate calls.
    This means that it...

    Read more
  • chao sun

    Please support GUI app in linux

  • Gabriel Silveira da Rosa

    Hi there, there is any suggestion by Microsoft about use previewers versions in production environment? I will create a new project in .NET Core, and start it using .NET Core 6 is an option, but i never used an “preview version” in this scenario.

  • Afshin Z

    It would be great if MAUI would support GTK+ as well, so we could target Linux desktop. Maybe .NET 7.

  • XLGeek Coder

    When I build single file using preview 1 or now 2 I still get mscordaccore.dll side by side with the exe. What is the property name to set for true single file package bundling? IncludeAllContentForSelfExtract seems to create temp folder in the background, so does IncludeNativeLibrariesForSelfExtract. Is there a way to load dependencies directly (i.e., w/o hitting the disk) like we did with assembly load event and embedded into the assembly resource...

    Read more
  • Andrew Stanton

    we’ve already been successful in trimming overheads from key areas involved in developers’ inner loops, focusing on various dotnet commands like new, build, and run

    If this doesnt also mean the same for the "F5 experience" it does not matter to me, or anyone else that would rather hit one key (F5 - the default key to build and start debugging in VS) instead of 11 "dotnet run[Enter]" or more.

    "inner loop" is a weird...

    Read more
  • Sinan Dinç

    Android and ios controls looks very different. This is very bad for consistency. Any plans to make a global css-like styling feature to override OS default styles and make the app look exactly the same on all platforms?