New .NET 6 APIs driven by the developer community

David Fowler

.NET 6 is on the way, and I wanted to share some of my favorite new APIs in .NET and ASP.NET Core that you are going to love. Why are you going to love them? Well because they were directly driven by our fantastic .NET developer community! Let’s get started!

Reading & Writing Files

In .NET 6, there’s a new low-level API to enable reading/writing of files without using a FileStream. It also supports scatter/gather IO (multiple buffers) and overlapping reads and writes at a given file offset.

using Microsoft.Win32.SafeHandles;
using SafeFileHandle handle = File.OpenHandle("ConsoleApp128.exe");
long length = RandomAccess.GetLength(handle);


Process Path & Id

There are a couple of new ways to access a process path and process id without allocating a new process object:

int pid = Environment.ProcessId;
string path = Environment.ProcessPath;


CSPNG (Cryptographically Secure Pseudorandom Number Generator)

Generating random numbers from a CSPNG (Cryptographically Secure Pseudorandom Number Generator) is a easier than ever:

// Give me 200 random bytes
byte[] bytes = RandomNumberGenerator.GetBytes(200);


We finally added Parallel.ForEachAsync, a way to schedule asynchronous work that allows you to control the degree of parallelism:

var urlsToDownload = new [] 

var client = new HttpClient();

await Parallel.ForEachAsync(urlsToDownload, async (url, token) =>
    var targetPath = Path.Combine(Path.GetTempPath(), "http_cache", url);

    HttpResponseMessage response = await client.GetAsync(url);

    if (response.IsSuccessStatusCode)
        using FileStream target = File.OpenWrite(targetPath);

        await response.Content.CopyToAsync(target);

Configuration Helpers

We added a helper to make it easier to throw if a required section of configuration is missing:

var configuration = new ConfigurationManager();
var options = new MyOptions();

// This will throw if the section isn't configured

class MyOptions
    public string? SettingValue { get; set;}


There’s a ton of new LINQ methods as well. It got lots of love in this release. Here’s a new helper to chunk any IEnumerable into batches:

int chunkNumber = 1;
foreach (int[] chunk in Enumerable.Range(0, 9).Chunk(3))
    Console.WriteLine($"Chunk {chunkNumber++}");
    foreach (var item in chunk)

Even More LINQ!

More LINQ! There are now MaxBy and MinBy methods:

var people = GetPeople();

var oldest = people.MaxBy(p => p.Age);
var youngest = people.MinBy(p => p.Age);

Console.WriteLine($"The oldest person is {oldest.Age}");
Console.WriteLine($"The youngest person is {youngest.Age}");

public record Person(string Name, int Age);

Power of 2

Don’t keep bit math in your head? Me neither. Here are some new helpers for working with powers of 2:

using System.Numerics;

uint bufferSize = 235;
if (!BitOperations.IsPow2(bufferSize))
    bufferSize = BitOperations.RoundUpToPowerOf2(bufferSize);


WaitAsync Improvements

There’s now a much easier (and properly implemented) way to wait for task to complete asynchronously. The following code will yield the await if it hasn’t completed in 10 seconds. The operation might still be running! This is for un-cancellable operations!

Task operationTask = SomeLongRunningOperationAsync();

await operationTask.WaitAsync(TimeSpan.FromSeconds(10));


No more having to check for null in every method before you throw an exception. It is now just one line of code.

void DoSomethingUseful(object obj)

Working with NativeMemory

If you want to use C APIs to allocate memory because you’re a l33t hacker or need to allocate native memory, then look no further. Don’t forget to free!

using System.Runtime.InteropServices;

    byte* buffer = (byte*)NativeMemory.Alloc(100);


Posix Signal Handling

Native support for Posix signal handling is here and we also emulate a couple of signals on Windows.

using System.Runtime.InteropServices;

var tcs = new TaskCompletionSource();

PosixSignalRegistration.Create(PosixSignal.SIGTERM, context =>
    Console.WriteLine($"{context.Signal} fired");

await tcs.Task;

New Metrics API

We added an entirely new metrics API based on @opentelemetry in .NET 6. It supports dimensions, is super efficient and will have exporters for popular metric sinks.

using System.Diagnostics.Metrics;

// This is how you produce metrics

var meter = new Meter("Microsoft.AspNetCore", "v1.0");
Counter<int> counter = meter.CreateCounter<int>("Requests");

var app = WebApplication.Create(args);

app.Use((context, next) =>
    counter.Add(1, KeyValuePair.Create<string, object?>("path", context.Request.Path.ToString()));
    return next(context);

app.MapGet("/", () => "Hello World");

You can even listen in and meter:

var listener = new MeterListener();
listener.InstrumentPublished = (instrument, meterListener) =>
    if(instrument.Name == "Requests" && instrument.Meter.Name == "Microsoft.AspNetCore")
        meterListener.EnableMeasurementEvents(instrument, null);

listener.SetMeasurementEventCallback<int>((instrument, measurement, tags, state) =>
    Console.WriteLine($"Instrument: {instrument.Name} has recorded the measurement: {measurement}");


Modern Timer API

Last but not least, a modern timer API (I think this is the 5th timer API in .NET now). It’s fully async and isn’t plagued by the types of gotchas the other timers are plagued with like object lifetime issues, no asynchronous callbacks etc.

var timer = new PeriodicTimer(TimeSpan.FromSeconds(1));

while (await timer.WaitForNextTickAsync())


This is just a sampling of the new APIs coming in .NET 6. For more information look at the .NET 6 release notes API diffs. Also, Stephen just wrote a spectacular blog on performance improvements in .NET6 so be sure to give that a read. Finally, don’t forget to download the .NET 6 Preview and try out the new APIs today.