Announcing .NET 6 Preview 4

Richard Lander

We are delighted to release .NET 6 Preview 4. We’re now about half-way through the .NET 6 release. It’s a good moment to look again at the full scope of .NET 6, much like the first preview post. Many features are in close-to-final form and others will come soon now that the foundational building blocks are in place for the release. Preview 4 establishes a solid base for delivering a final .NET 6 build in November, with finished features and experiences. It’s also ready for real world testing if you haven’t yet tried .NET 6 in your environment.

Speaking of the final release, we now have a date! Book off November 9-11 for .NET Conf 2021. We’ll launch .NET 6 on the 9th with many in-depth talks and demos that tell you everything you want to know about .NET 6.

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

See the ASP.NET Core and EF Core posts for more detail on what’s new for web and data access scenarios. There’s also new .NET MAUI post that describe new client app experiences and a Hot reload post that describes a new approach to developer productivity.

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

Build 2021

The Microsoft Build conference is this week. It’s free and streaming on the web. It’s also not too late to register.

You’ll want to checkout these talks for sure, which will include lots of discussion of .NET 6 and demos that show you what’s new and now possible.

.NET 6 Themes

We started planning .NET 6 in late 2020 on GitHub. We identified eight themes across a wide set of topics, including industry scenarios, support, and education. The themes represent half to three quarters of our effort for the release. There are many projects that don’t rise to the level of a theme or that are significant but not thematic (like supporting Apple Silicon devices).

The following are the .NET 6 themes, each described with a one sentence summary. They are listed in the same order they are displayed in

Some of these themes are discussed in more detail in the following posts:

.NET Platform unification

We’ve talked a lot in past posts and at conferences about .NET unification yet it is missing from the themes. Platform unification is baked into everything we do and has no need for its own theme. One can think of it as being the one mega-theme above and beyond the ones that are listed. It is interleaved through multiple of the themes and is a basic assumption of the team going forward.

The inner-loop performance project is a great example. It assumes that .NET 6 apps all share the same foundation, for example using the same build system and libraries. Where there is a technical difference, like using a different runtime (CoreCLR or Mono) or code generation technique (AOT or JIT), we take those things into account and deliver pragmatic and appropriate experiences, with a bias to no observable experience difference. The EventPipe project is another similar example.

Production confidence

We’ll soon start releasing “go live” builds that are supported in production. We’re currently targeting August for that. Our development model is oriented around enabling production workloads, even while we’re finishing up work on all the themes that were just mentioned.

Production confidence begins with the site. It’s been running half its site load on .NET 6 starting with Preview 1. While not massive scale, it is a mission critical site for our team and we take it very seriously. .NET 6 has been working for us like a champ.

We also work with Microsoft teams who deploy their production apps on .NET previews. They do that so they can take advantage of new .NET features early. These teams are always looking for opportunities to reduce their cloud hosting costs, and deploying new .NET versions has proven to be one of the most effective and lowest effort approaches for that. These teams give us early feedback that helps us ensure new features are ready for global production use. They also significantly influence final feature shape because they are our first production users.

All of this early battle-testing with real-world apps builds our confidence that .NET 6 will be ready for running your app.

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

Tools: Hot Reload with the Visual Studio debugger and dotnet CLI

Hot Reload is a new experience that enables you to make edits to your app’s source code while it is running without needing to manually pause the app or hit a breakpoint. Hot Reload improves developer productivity by reducing the number of times you need to restart your running app.

With this release, Hot Reload works for many types of apps such as WPF, Windows Forms, WinUI, ASP.NET, Console Apps and other frameworks that are running on top of the CoreCLR runtime. We’re also working to bring this technology to WebAssembly, iOS and Android apps that run on top of Mono, but this is still coming (in a later Preview).

To start testing this feature install Visual Studio 2019 version 16.11 Preview 1 and start your app with the Visual Studio debugger (F5). Once your app is running, you’ll now have the new option to make code changes and apply them using the new “apply code changes” button as illustrated below.

.NET Hot Reload Apply Code Changes in Visual Studio 2019

Note: Click the image to start the animation.

Hot Reload is also available through the dotnet watch tool. Preview 4 includes multiple fixes that improve that experience.

If you want to learn more about Hot Reload you can read Introducing .NET Hot Reload.

System.Text.Json support for IAsyncEnumerable

IAsyncEnumerable<T> is an important feature that was added with .NET Core 3.0 and C# 8. The new enhancements enable System.Text.Json (de)serialization with IAsyncEnumerable<T> objects.

The following examples use streams as a representation of any async source of data. The source could be files on a local machine, or results from a database query or web service API call.

Streaming serialization

System.Text.Json now supports serializing IAsyncEnumerable<T> values as JSON arrays, as you can see in the following example.

using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;

static async IAsyncEnumerable<int> PrintNumbers(int n)
    for (int i = 0; i < n; i++) yield return i;

using Stream stream = Console.OpenStandardOutput();
var data = new { Data = PrintNumbers(3) };
await JsonSerializer.SerializeAsync(stream, data); // prints {"Data":[0,1,2]}

IAsyncEnumerable values are only supported using the asynchronous serialization methods. Attempting to serialize using the synchronous methods will result in a NotSupportedException being thrown.

Streaming deserialization

Streaming deserialization required a new API that returns IAsyncEnumerable<T>. We added the JsonSerializer.DeserializeAsyncEnumerable method for this purpose, as you can see in the following example.

using System;
using System.IO;
using System.Text;
using System.Text.Json;

var stream = new MemoryStream(Encoding.UTF8.GetBytes("[0,1,2,3,4]"));
await foreach (int item in JsonSerializer.DeserializeAsyncEnumerable<int>(stream))

This example will deserialize elements on-demand and can be useful when consuming particularly large data streams. It only supports reading from root-level JSON arrays, although that could be relaxed in the future based on feedback.

The existing DeserializeAsync method nominally supports IAsyncEnumerable<T>, but within the confines of its non-streaming method signature. It must return the final result as a single value, as you can see in the following example.

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.Json;

var stream = new MemoryStream(Encoding.UTF8.GetBytes(@"{""Data"":[0,1,2,3,4]}"));
var result = await JsonSerializer.DeserializeAsync<MyPoco>(stream);
await foreach (int item in result.Data)

public class MyPoco
    public IAsyncEnumerable<int> Data { get; set; }

In this example, the deserializer will have buffered all IAsyncEnumerable contents in memory before returning the deserialized object. This is because the deserializer needs to have consumed the entire JSON value before returning a result.

System.Text.Json: Writable DOM Feature

The writeable JSON DOM feature adds a new straightforward and high-performance programming model for System.Text.Json. This new API is attractive since it avoids the complexity and ceremony of serialization and the traditional cost of a DOM.

This new API has the following benefits:

  • A lightweight alternative to serialization for cases when use of POCO types is not possible or desired, or when a JSON schema is not fixed and must be inspected.
  • Enables efficient modification of a subset of a large tree. For example, it is possible to efficiently navigate to a subsection of a large JSON tree and read an array or deserialize a POCO from that subsection. LINQ can also be used with that.
  • Enables using the C# dynamic keyword, which allows for a loosely-typed, more script-like model.

We’re looking for feedback on support for dynamic. Please give us your feedback if dynamic support is important to you.

More details are available at dotnet/runtime #6098.

Writeable DOM APIs

The writeable DOM exposes the following types.

namespace System.Text.Json.Node
    public abstract class JsonNode {...};
    public sealed class JsonObject : JsonNode, IDictionary<string, JsonNode?> {...}
    public sealed class JsonArray : JsonNode, IList<JsonNode?> {...};
    public abstract class JsonValue : JsonNode {...};

Example code

The following example demonstrates the new programming model.

    // Parse a JSON object
    JsonNode jNode = JsonNode.Parse("{"MyProperty":42}");
    int value = (int)jNode["MyProperty"];
    Debug.Assert(value == 42);
    // or
    value = jNode["MyProperty"].GetValue<int>();
    Debug.Assert(value == 42);

    // Parse a JSON array
    jNode = JsonNode.Parse("[10,11,12]");
    value = (int)jNode[1];
    Debug.Assert(value == 11);
    // or
    value = jNode[1].GetValue<int>();
    Debug.Assert(value == 11);

    // Create a new JsonObject using object initializers and array params
    var jObject = new JsonObject
        ["MyChildObject"] = new JsonObject
            ["MyProperty"] = "Hello",
            ["MyArray"] = new JsonArray(10, 11, 12)

    // Obtain the JSON from the new JsonObject
    string json = jObject.ToJsonString();
    Console.WriteLine(json); // {"MyChildObject":{"MyProperty":"Hello","MyArray":[10,11,12]}}

    // Indexers for property names and array elements are supported and can be chained
    Debug.Assert(jObject["MyChildObject"]["MyArray"][1].GetValue<int>() == 11);

Microsoft.Extensions.Logging compile-time source generator

.NET 6 introduces the LoggerMessageAttribute type. This attribute is part of the Microsoft.Extensions.Logging namespace, and when used, it source-generates performant logging APIs. The source-generation logging support is designed to deliver a highly usable and highly performant logging solution for modern .NET applications. The auto-generated source code relies on the ILogger interface in conjunction with LoggerMessage.Define functionality.

The source generator is triggered when LoggerMessageAttribute is used on partial logging methods. When triggered, it is either able to autogenerate the implementation of the partial methods it’s decorating, or produce compile-time diagnostics with hints about proper usage. The compile-time logging solution is typically considerably faster at run time than existing logging approaches. It achieves this by eliminating boxing, temporary allocations, and copies to the maximum extent possible.

There are benefits over manually using LoggerMessage.Define APIs directly:

  • Shorter and simpler syntax: Declarative attribute usage rather than coding boilerplate.
  • Guided developer experience: The generator gives warnings to help developers do the right thing.
  • Support for an arbitrary number of logging parameters. LoggerMessage.Define supports a maximum of six.
  • Support for dynamic log level. This is not possible with LoggerMessage.Define alone.

If you would like to keep track of improvements and known issues, see dotnet/runtime#52549.

Basic usage

To use the LoggerMessageAttribute, the consuming class and method need to be partial. The code generator is triggered at compile time, and generates an implementation of the partial method.

public static partial class Log
    [LoggerMessage(EventId = 0, Level = LogLevel.Critical, Message = "Could not open socket to `{hostName}`")]
    public static partial void CouldNotOpenSocket(ILogger logger, string hostName);

In the preceding example, the logging method is static and the log level is specified in the attribute definition. When using the attribute in a static context, the ILogger instance is required as a parameter. You may choose to use the attribute in a non-static context as well. For more examples and usage scenarios visit the docs for the compile-time logging source generator.

System.Linq enhancements

New System.LINQ APIs have been added that have been requested and contributed by the community.

Enumerable support for Index and Range parameters

The Enumerable.ElementAt method now accepts indices from the end of the enumerable, as you can see in the following example.

Enumerable.Range(1, 10).ElementAt(^2); // returns 9

An Enumerable.Take overload has been added that accepts Range parameters. It simplifies taking slices of enumerable sequences:

  • source.Take(..3) instead of source.Take(3)
  • source.Take(3..) instead of source.Skip(3)
  • source.Take(2..7) instead of source.Take(7).Skip(2)
  • source.Take(^3..) instead of source.TakeLast(3)
  • source.Take(..^3) instead of source.SkipLast(3)
  • source.Take(^7..^3) instead of source.TakeLast(7).SkipLast(3).

Credit to @dixin for contributing the implementation.


The TryGetNonEnumeratedCount method attempts to obtain the count of the source enumerable without forcing an enumeration. This approach can be useful in scenarios where it is useful to preallocate buffers ahead of enumeration, as you can see in the following example.

List<T> buffer = source.TryGetNonEnumeratedCount(out int count) ? new List<T>(capacity: count) : new List<T>();
foreach (T item in source)

TryGetNonEnumeratedCount checks for sources implementing ICollection/ICollection<T> or takes advantage of some of the internal optimizations employed by Linq.


New variants have been added to the set operations that allow specifying equality using key selector functions, as you can see in the following example.

Enumerable.Range(1, 20).DistinctBy(x => x % 3); // {1, 2, 3}

var first = new (string Name, int Age)[] { ("Francis", 20), ("Lindsey", 30), ("Ashley", 40) };
var second = new (string Name, int Age)[] { ("Claire", 30), ("Pat", 30), ("Drew", 33) };
first.UnionBy(second, person => person.Age); // { ("Francis", 20), ("Lindsey", 30), ("Ashley", 40), ("Drew", 33) }


MaxBy and MinBy methods allow finding maximal or minimal elements using a key selector, as you can see in the following example.

var people = new (string Name, int Age)[] { ("Francis", 20), ("Lindsey", 30), ("Ashley", 40) };
people.MaxBy(person => person.Age); // ("Ashley", 40)


Chunk can be used to chunk a source enumerable into slices of a fixed size, as you can see in the following example.

IEnumerable<int[]> chunks = Enumerable.Range(0, 10).Chunk(size: 3); // { {0,1,2}, {3,4,5}, {6,7,8}, {9} }

Credit to Robert Andersson for contributing the implementation.

FirstOrDefault/LastOrDefault/SingleOrDefault overloads taking default parameters

The existing FirstOrDefault/LastOrDefault/SingleOrDefault methods return default(T) if the source enumerable is empty. New overloads have been added that accept a default parameter to be returned in that case, as you can see in the following example.

Enumerable.Empty<int>().SingleOrDefault(-1); // returns -1

Credit to @Foxtrek64 for contributing the implementation.

Zip overload accepting three enumerables

The Zip method now supports combining three enumerables, as you can see in the following example.

var xs = Enumerable.Range(1, 10);
var ys = xs.Select(x => x.ToString());
var zs = xs.Select(x => x % 2 == 0);

foreach ((int x, string y, bool z) in Enumerable.Zip(xs,ys,zs))

Credit to Huo Yaoyuan for contributing the implementation.

Significantly improved FileStream performance on Windows

FileStream has been re-written in .NET 6, to have much higher performance and reliability on Windows.

The re-write project has been phased over five PRs:

The final result is that FileStream never blocks when created for async IO, on Windows. That’s a major improvement. You can observe that in the benchmarks, which we’ll look at shortly.


The first PR enables FileStream to choose an implementation at runtime. The most obvious benefit of this pattern is enabling switching back to the old .NET 5 implementation, which you can do with the following setting, in runtimeconfig.json.

    "configProperties": {
        "System.IO.UseNet5CompatFileStream": true

We plan to add an io_uring strategy next, which takes advantage of a Linux feature by the same name in recent kernels.

Performance benchmark

Let’s measure the improvements using BenchmarkDotNet.

public class FileStreamPerf
    private const int FileSize = 1_000_000; // 1 MB
    private Memory<byte> _buffer = new byte[8_000]; // 8 kB

    [GlobalSetup(Target = nameof(ReadAsync))]
    public void SetupRead() => File.WriteAllBytes("file.txt", new byte[FileSize]);

    public async ValueTask ReadAsync()
        using FileStream fileStream = new FileStream("file.txt", FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true);
        while (await fileStream.ReadAsync(_buffer) > 0)

    public async ValueTask WriteAsync()
        using FileStream fileStream = new FileStream("file.txt", FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 4096, useAsync: true);
        for (int i = 0; i < FileSize / _buffer.Length; i++)
            await fileStream.WriteAsync(_buffer);

    public void Cleanup() => File.Delete("file.txt");

BenchmarkDotNet=v0.13.0, OS=Windows 10.0.18363.1500 (1909/November2019Update/19H2)
Intel Xeon CPU E5-1650 v4 3.60GHz, 1 CPU, 12 logical and 6 physical cores
.NET SDK=6.0.100-preview.5.21267.9
  [Host]     : .NET 5.0.6 (5.0.621.22011), X64 RyuJIT
  Job-OIMCTV : .NET 5.0.6 (5.0.621.22011), X64 RyuJIT
  Job-CHFNUY : .NET 6.0.0 (, X64 RyuJIT
Method Runtime Mean Ratio Allocated
ReadAsync .NET 5.0 3.785 ms 1.00 39 KB
ReadAsync .NET 6.0 1.762 ms 0.47 1 KB
WriteAsync .NET 5.0 12.573 ms 1.00 39 KB
WriteAsync .NET 6.0 3.200 ms 0.25 1 KB

Environment: Windows 10 with SSD drive with BitLocker enabled


  • Reading 1 MB file is now 2 times faster, while writing is 4 times faster.
  • Memory allocations dropped from 39 kilobytes to 1 kilobyte! This is a 97.5% improvement!

These changes should provide a dramatic improvement for FileStream users on Windows. More details are available at dotnet/core #6098.

Enhanced Date, Time and Time Zone support

The following improvements have been made to date and time related types.

New DateOnly and TimeOnly structs

Date- and time-only structs have been added, with the following characteristics:

  • Each represent one half of a DateTime, either only the date part, or only the time part.
  • DateOnly is ideal for birthdays, anniversary days, and business days. It aligns with SQL Server’s date type.
  • TimeOnly is ideal for recurring meetings, alarm clocks, and weekly business hours. It aligns with SQL Server’s time type.
  • Complements existing date/time types (DateTime, DateTimeOffset, TimeSpan, TimeZoneInfo).
  • In System namespace, shipped in CoreLib, just like existing related types.

Perf improvements to DateTime.UtcNow

This improvement has the following benefits:

  • Fixes 2.5x perf regression for getting the system time on Windows.
  • Utilizes a 5-minute sliding cache of Windows leap second data instead of fetching with every call.

Support for both Windows and IANA time zones on all platforms

This improvements has the following benefits:

  • Implicit conversion when using TimeZoneInfo.FindSystemTimeZoneById (
  • Explicit conversion through new APIs on TimeZoneInfo: TryConvertIanaIdToWindowsId, TryConvertWindowsIdToIanaId, and HasIanaId (
  • Improves cross-plat support and interop between systems that use different time zone types.
  • Removes need to use TimeZoneConverter OSS library. The functionality is now built-in.

Improved time zone display names

This improvement has the following benefits:

  • Removes ambiguity from the display names in the list returned by TimeZoneInfo.GetSystemTimeZones.
  • Leverages ICU / CLDR globalization data.
  • Unix only for now. Windows still uses the registry data. This may be changed later.


  • The UTC time zone’s display name and standard name were hardcoded to English and now uses the same language as the rest of the time zone data (CurrentUICulture on Unix, OS default language on Windows).
  • Time zone display names in WASM use the non-localized IANA ID instead, due to size limitations.
  • TimeZoneInfo.AdjustmentRule nested class gets its BaseUtcOffsetDelta internal property made public, and gets a new constructor that takes baseUtcOffsetDelta as a parameter. (
  • TimeZoneInfo.AdjustmentRule also gets misc fixes for loading time zones on Unix (, (


The following improvements have been made to the RyuJIT compiler.

Community contributions

@SingleAccretion has been busy making the following improvements over the last few months. That is in addition to a contribution in .NET 6 Preview 3. Thanks!

Dynamic PGO

The following improvements have been made to support dynamic PGO.

JIT Loop Optimizations

The following improvements have been made for loop optimizations.


The following improvements have been made to Linear Scan Register Allocation (LRSA).


.NET Diagnostics: EventPipe for Mono and Improved EventPipe Performance

EventPipe is .NET’s cross-platform mechanism for egressing events, performance data, and counters. Starting with .NET 6, we’ve moved the implementation from C++ to C. With this change, Mono will be able to use EventPipe as well! This means that both CoreCLR and Mono will use the same eventing infrastructure, including the .NET Diagnostics CLI Tools! This change also came with small reduction in size for CoreCLR:

lib after size – before size diff 7037856 – 7049408 -11552

We’ve also made some changes that improve EventPipe throughput while under load. Over the first few previews, we’ve made a series of changes that result in throughput improvements as high as 2.06x what .NET 5 was capable of: image

Data collected using the EventPipeStress framework in dotnet/diagnostics. The writer app writes events as fast as it can for 60 seconds. The number of successful and dropped events is recorded.

For more information, see dotnet/runtime #45518.

IL trimming

Warnings enabled by default

Trim warnings tell you about places where trimming may remove code that’s used at runtime. These warnings were previously disabled by default because the warnings were very noisy, largely due to the .NET platform not participating in trimming as a first class scenario.

We’ve annotated large portions of the .NET libraries (the runtime libraries, not ASP.NET Core or Windows Desktop frameworks) so that they produce accurate trim warnings. As a result, we felt it was time to enable trimming warnings by default.

You can disable warnings by setting <SuppressTrimAnalysisWarnings> to true. With earlier releases, you can set the same property to false to see the trim warnings.

Trim warnings bring predictability to the trimming process and put power in developers’ hands. We will continue annotating more of the .NET libraries, including ASP.NET Core in subsequent releases. We hope the community will also improve the trimming ecosystem by annotating more code to be trim safe.

More information:

The new default Trim Mode in .NET 6 is link. The link TrimMode can provide significant savings by trimming not just unused assemblies, but also unused members.

In .NET 5, trimming tried to find and remove unreferenced assemblies by default. This is safer, but provides limited benefit. Now that trim warnings are on by default developers can be confident in the results of trimming.

Let’s take a look at this trimming improvement by trimming one of the .NET SDK tools, as an example. I’m going to use crossgen, the Ready To Run compiler. It can be trimmed with only a few trim warnings, which the crossgen team was able to resolve.

First, let’s look at publishing crossgen as a self-contained app without trimming. It is 80 MB (which includes the .NET runtime and all the libraries).


We can then try out the (now legacy) .NET 5 default trim mode, copyused. The result drops to 55 MB.


The new .NET 6 default trim mode,link, drops the self-contained file size much further, to 36MB.


We hope that the new link trim mode aligns much better with the expectations for trimming: significant savings and predictable results.

Shared model with Native AOT

We’ve implemented the same trimming warnings for the Native AOT experiment as well, which should improve the Native AOT compilation experience in much the same way.

Single-file publishing

The following improvements have been made for single-file application publishing.

Static Analysis

Analyzers for single-file publishing were added in .NET 5 to warn about Assembly.Location and a few other APIs which behave differently in single-file bundles.

For .NET 6 Preview 4 we’ve improved the analysis to allow for custom warnings. If you have an API which doesn’t work in single-file publishing you can now mark it with the [RequiresAssemblyFiles] attribute, and a warning will appear if the analyzer is enabled. Adding that attribute will also silence all warnings related to single-file in the method, so you can use the warning to propagate warnings upward to your public API.

The analyzer is automatically enabled for exe projects when PublishSingleFile is set to true, but you can also enabled it for any project by setting EnableSingleFileAnalysis to true. This is could be helpful if you want to embed a library in a single file bundle.


Single-file bundles now support compression, which can be enabled by setting the property EnableCompressionInSingleFile to true. At runtime, files are decompressed to memory as necessary. Compression can provide huge space savings for some scenarios.

Let’s look at single file publishing, with and without compression, used with NuGet Package Explorer.

Without compression: 172 MB


With compression: 71.6 MB


Compression can significantly increase the startup time of the application, especially on Unix platforms (because they have a no-copy fast start path that can’t be used with compression). You should test your app after enabling compression to see if the additional startup cost is acceptable.

PublishReadyToRun now uses crossgen2 by default

Crossgen2 is now enabled by default when publishing ReadyToRun images. It also optionally supports generating composite images.

The following setting are exposed to enable you to configure publishing with ready to run code. The settings are set to their default values.

    <!-- set to true to enable publishing with ready to run native code -->
    <!-- set to false to use crossgen like in 5.0 -->
    <!-- set to true to generate a composite R2R image -->

CLI install of .NET 6 SDK Optional Workloads

.NET 6 will introduce the concept of SDK workloads that can be install after the fact on top of the .NET SDK to enable various scenarios. The new workloads available in preview 4 are .NET MAUI and Blazor WebAssembly AOT workloads.

For the .NET MAUI workloads, we still recommend using the maui-check tool for preview 4 as it includes additional components not yet available in Visual Studio or as an .NET SDK workload. To try out the .NET SDK experience anyway (using iOS as the example), run dotnet workload install microsoft-ios-sdk-full. Once installed, you can run dotnet new ios and then dotnet build to create and build your project.

For Blazor WebAssembly AOT, follow the installation instructions provided via the ASP.NET blog.

Preview 4 includes .NET MAUI workloads for iOS, Android, tvOS, MacOS, and MacCatalyst.

Note that dotnet workload install copies the workloads from into your SDK install so will need to be run elevated/sudo if the SDK install location is protected (meaning at an admin/root location).

Built-in SDK version checking

To make it easier to track when new versions of the SDK and Runtimes are available, we’ve added a new command to the .NET 6 SDK: dotnet sdk check

This will tell you within each feature band what is the latest available version of the .NET SDK and .NET Runtime.


CLI Templates (dotnet new)

Preview 4 introduces a new search capability for templates. dotnet new --search will search for matching templates. During upcoming previews the data used for this search will be updated more frequently.

Templates installed in the CLI are available for both the CLI and Visual Studio. An earlier problem with user installed templates being lost when a new version of the SDK was installed has been resolved, however templates installed prior to .NET 6 Preview 4 will need to be reinstalled.

Other improvements to template installation include support for the --interactive switch to support authorization credentials for private NuGet feeds.

Once CLI templates are installed, you can check if updates are available via --update-check and --update-apply. This will now reflect template updates much more quickly, support the NuGet feeds you have defined, and support --interactive for authorization credentials.

In Preview 4 and upcoming Previews, the output of dotnet new commands will be cleaned up to focus on the information you need most. For example, the dotnet new --install <package> lists only the templates just installed, rather than all templates.

To support these and upcoming changes to dotnet new, we are making significant changes to the Template Engine API that may affect anyone hosting the template engine. These changes will appear in Preview 4 and Preview 5. If you are hosting the template engine, please connect with us at so we can work with you to avoid or minimize disruption.


.NET 6 will be 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.

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.


We’re well into the .NET 6 release at this point. While the final release in November still seems like a long way off, we’re getting close to being done feature development. Now is a great time for feedback since the shape of the new features are now established and we’re still in the active development phase so can readily act on that feedback.

Speaking of November, please book off some time during November 9-11 to watch .NET Conf 2021. It is certain to be exciting and fun. We’ll be releasing the final .NET 6 build on November 9th, and a blog post even longer than this one.

Still looking for more to read? You might check out our new conversations series. There is a lot of detailed insights about new .NET 6 features.

We hope you enjoy trying out preview 4.


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

  • Max Mustermueller 0

    When does ZIP gets async methods for zipping and unzipping? There is a issue/request on Github with many votes since many years now. People are constantly asking for updates on it but the .NET team completely ignores it. //edit: here is the issue

    Still looking forward for polymorphic JSON support. I mean it has been said its a goal for .NET6, is it still? Or has it been (silently) dropped?

    FYI: The new trimming in .NET 6 completely broke trimming on WPF (before it was possible although its not officially supported). It would be nice if WPF/Winforms get trimming support. They support .NET (Core) and they are bigger than console projects. It’s sad how both, very especially WPF, is now in maintenance mode after MAUI was announced which still takes many years before it can be used in productive environments.

    • Andy GockeMicrosoft employee 0

      Hi Max, you’re correct — trimming WPF or WinForms is not supported or recommended in .NET 6. It’s surprising that you could trim your WPF in .NET 5 and your app still worked; during testing we found that even some of the basic WPF template apps did not work when trimmed in .NET 5.

      There’s effectively no code change here in .NET 6. WPF has not made any changes to become trim-compatible, and trimming is continuing to follow the original design, but with improvements through warnings.

      Trimming WPF will require significant changes to WPF to annotate or replace complex reflection functionality, but if you’re interested in the feature I would recommend following along at

      • anonymous 0

        this comment has been deleted.

      • Max Mustermueller 0

        All it needs was to exclude a few files only, see but now this isn’t enough. Even if I switch the trim mode back to copyused and even if I exclude all missing files so exaclty the same files on .NET6 publish exist as on .NET5 publish, the application still crashes on start (Someone posted a solution for .NET6 but this doesn’t work on larger projects.)

        So something must have changed on “copyused” trimming that breaks applications. I would love to report it but it would just get closed instantly when someone see’s “WPF” in it. Now, in order to keep a decent application size, I have to stay on .NET5 (forever) until WPF gets trim support, which, by seeing WPF’s team activity, can be years.

        Its kinda frustrating that Microsoft tells about unifying and reunions and stuff like that and then keeps large .NET eco systems completely out. Its not just trimming but also AOT and other stuff. For .NET it just shows that nothing have changed at all. Its still highly fragmented and companies might choose another programming language in order to be flexible enough.

        • Andy GockeMicrosoft employee 0

          That was definitely not all that was necessary — I tried a number of WPF apps that are in our compat suite and most of them did not work in .NET 5, even with workarounds. It may be that you got your application to work, but that was purely by accident.

          The way that WPF was built unfortunately just requires a lot of hard work in going through the warnings (linked above) and resolving the problems. Until then you can never be sure that trimming will not result in something being trimmed away.

          It’s important to realize that this is a runtime feature, and not all types of code are immediately compatible. Investment in the feature brings new capabilities and form factors to .NET, but it doesn’t automatically mean that every app is compatible, in the same way that bringing the core .NET framework to Linux does not make every .NET app automatically compatible with Linux.

          • Max Mustermueller 0

            First of all nobody spoke about “automatically compatible” and second, Linux is a different OS and WPF was developed for Windows. Its perfect clear to me that WPF doesn’t support Linux. But runtime features, such as nullable, trimming, AOT. That is something that requires afford yes, even of the .NET (Core) team has to update their repo, which speaking about nullable was also much work, but that is something I would expect. At least that someone starts doing the work. As for now AOT and trimming is just on paper for WPF and nobody knows if it comes ever.

            Also speaking about investments WPF team tries to support ARM64 now, it wasn’t “automatically compatible”. Why not doing the same for trimming and AOT? Companies cannot trust into .NET when its doing things randomly. Where is consistency? Where the unification? Its a fragmented place and roadmap is completely unclear. Would you invest into something like that as a company?

          • Charles Roddie 0

            Max, having to get everything up to speed at once would just block progress. Of the 3 windows platforms (WPF, UWP, WinForms), WPF is the hardest (requirements: and ) but there is ongoing work by kant2002 is bringing all this closer.

            Microsoft should, however, recognize that AOT is critical to app development in .Net, and move to productize NativeAOT, and employ kant2002 to make sure he keeps working on it! All the important work for .Net on Windows at the moment is being done by kant2002!

          • Richard LanderMicrosoft employee 0

            Also speaking about investments WPF team tries to support ARM64 now, it wasn’t “automatically compatible”. Why not doing the same for trimming and AOT?

            That’s a really great question. WPF supporting Arm64 is MUCH easier than enabling trimming. It’s not like we only take on easy problems, but this one is really a lot of work. The issue is that WPF is built on managed C++ (AKA C++/CLI). That technology is never going to be supported by the assembly trimmer. The team needs to convert that codebase to C# The other issue is that XAML is pretty tough to reason about. It’s another set of roots on managed types. The assembly trimmer is not fluent in XAML. Also, to my understanding, much of the XAML processing results in Reflection anyway, which also doesn’t work well for trimming, although that’s largely a red herring on the XAML problem, generally. If the WPF team wrote a C# source generator for XAML, then that would work. Last, it’s probably the case that you have to solve both of these problems to get anywhere useful, such that there isn’t an attractive phasing of the work that gets you value along the way.

            I may have some of this wrong, which I’m sure Andy or others can correct me on.

    • Eirik George Tsarpalis 0

      JSON polymorphic support is still planned for .NET 6, but we’re still in an early prototyping phase. At this point we can’t make any promises about the feature making it for .NET 6.

      • Max Mustermueller 0

        Thanks for clarification, I haven’t seen a pull request yet. Let’s hope it makes it into .NET6

  • Marina Sundström 0

    I am still having issues on my M1 Mac. I cannot figure out if it is mainly because of some side-by-side issue, or not.
    Too sad to see that you did not fix this before Build.

    robert@Roberts-MBP ShellApp % tye run
    Failed to load /usr/local/share/dotnet/host/fxr/6.0.0-preview.4.21253.7/libhostfxr.dylib, error: dlopen(/usr/local/share/dotnet/host/fxr/6.0.0-preview.4.21253.7/libhostfxr.dylib, 1): no suitable image found.  Did find:
        /usr/local/share/dotnet/host/fxr/6.0.0-preview.4.21253.7/libhostfxr.dylib: mach-o, but wrong architecture
        /usr/local/share/dotnet/host/fxr/6.0.0-preview.4.21253.7/libhostfxr.dylib: mach-o, but wrong architecture
    The library libhostfxr.dylib was found, but loading it from /usr/local/share/dotnet/host/fxr/6.0.0-preview.4.21253.7/libhostfxr.dylib failed
      - Installing .NET prerequisites might help resolve this problem.
    • Richard LanderMicrosoft employee 0

      Today, you have to use .NET either all x64 or all Arm64 w/no side by side. We’re working on fixing that. See:

      Too sad to see that you did not fix this before Build.

      No one is more sad than me. I sympathize.

      • Antonio Johnson 0

        I get this exact same error message on my M1. Are you saying that in order to be able to use it, I need to have one of the following?

        Visual Studio Mac + x64 .net 6 SDK
        VS Code arm64 + .arm64 .net 6 SDK?

        • Richard LanderMicrosoft employee 0


          That’s not great, but we’re still working on our M1 / Apple Silicon support. We’re clearly at the last mile, but down to installer and other challenges.

          • Antonio Johnson 0

            I was able to get this to work with visual studio mac by uninstalling the arm64 SDK and switching to Visual Studio Mac preview channel.

            I was however, unable to get this to work with VS Code and Arm64 SDK. That’s with removing visual studio Mac and removing all .net SDKs before installing the Arm64 SDK (dotnet 6, Preivew 4).

            Is there something I’m missing?

            Thanks for all of the hard work on this.

            Edit: My apologies, I should have been more clear on what I meant by “doesn’t work”. I’m talking about being unable to debug, and even with the beta build of OmniSharp I get the following error:

            Unable to attach to CoreCLR. Unknown Error: 0x80131c4e

  • Sepp Mackmuns 0

    Hi, speaking of Windows ARM64 support for Desktop (WinForms/WPF), is this still planned to be ported to .NET 5 within the first half of 2021?

    • Richard LanderMicrosoft employee 0

      I asked someone from the WPF team to answer with latest status.

  • Eugene Ivanoff 0

    As for JSON: writable DOM and dynamic are great additions! Now working with JSON will be more pleasant. Thanks!

  • Tony Spencer 0

    I didn’t see anything about Blazor Desktop… is it renamed to hybrid Blazor experiences or is it something completely different?

  • Daniel Hughes 0

    Does the .net unification mean that Xamarin platforms will use the same base class libraries as .net on windows. Currently we have run into a number of issues where .net standard API’s are simply not available on Xamarin (throw PlatformNotSupportedExceptions or simply do not work). For example AesGcm, CertificateRequest and HttpClient.ClientCertificate support.

    • Richard LanderMicrosoft employee 0


      The answer is a tiny bit more nuanced. First, it’s not “.NET on Windows”, but “.NET 6 cross-platform libraries, the ones in dotnet/runtime”. Second, there may always be a different behavior for certain APIs in iOS vs Windows vs Linux, depending on what the OS enables. I don’t know if that’s an issue for the APIs you mentioned, but raising that there are some OS differences, but it’s no longer a Mono vs CoreCLR ecosystem issue, which is I think what you were getting at.

  • Daniel Hughes 0

    I see that installers exist for everything except linux.

    The last time this happened the linux installer was completely broken on full release because no one from the community was actually able to test it until it was to late due to the previews not including it.

    • Richard LanderMicrosoft employee 0

      There are no Linux preview installers. Yes, this is still a gap that we still intend to fix (just not yet).

      It’s not a function of limited investment in Linux. We’re investing a ton in our source build project to enable distros to include .NET themselves.

  • 添昊 栾 0

    what is the 3D solution for .net6? except unity, we need kind of three.js\QT 3D something to draw 3d models in our app. i can see wave engine on the way, but no docoment and bugs everywhere, will microsoft ship official 3d solutions that can embed in maui that can run on android/ios/windows/mac/web and also for blazor?

    • Richard LanderMicrosoft employee 0

      I don’t think we have anything like that. Yes, I’d suggest Unity for that need, and people (not just gamer writers) successfully use Unity for this purpose.

      I’m not familiar with the wave engine.

      You might check if Project Uno has 3D modeling capabilities. I’m not sure.

      • Paulo Pinto 0

        WPF has 3D support, but apparently the way forward is for us to either learn C++ and DirectX, or rely on third parties.

        Compare this hurdle with Java, Swift, Flutter, Android on their respective frameworks.

      • 添昊 栾 0

        What a sad news, Unity has no plan for that

        • yoli avalon 0

          MonoGame is 3d game framework, the spiritual successor to XNA

    • Gavin Williams 0

      For accessing Direct3d via .Net I would rate the following technologies:

      Low Level
      * Hand write native DirectX in a C++/WinRT Component, access from C#. Difficult and time consuming, but futureproof, and full DirectX access.
      * TerraFX @ – a binding to DirectX and can be used in C#, but it’s not a proper C# wrapper, using unsafe and C++ style.
      * SharpDX – retired but a ready to use tool and the best experience for C# dev’s in the low-level category. [a]

      * Unity –
      * Stride –

      There are some other projects in the works, but we have to see which ones reach maturity and adoption.

      [a] I’m not sure how it’s going to handle the new .Net, anyone else know?

  • Reelix 0

    Are .MinBy / .MaxBy faster than just going .OrderBy/OrderByDescending(x => x.Age).First(), or do they do that under the hood?

    • Branimir Ricko 0

      OrderBy is of O(N*log(N)) complexity. MinBy and MaxBy can be of O(N) complexity so it would not make sense to implement Max/Minby using OrderBy

  • Brooks MYM 0

    Good job for hard work on .net.
    Still no VS 2022 preview 1 out on build 2021 🙂
    1、Python script support(AddIn and Function) in Excel like Power Fx
    2、.NET NIO/Sockets framework support like Netty/SwiftNIO

Feedback usabilla icon