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.


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

  • Richard Deeming 0

    I think some of the XML doc comments could do with a proof-reader! 🙂

    For example: Chunk

    An IEnumerable{T} whose elements to chunk.
    • silkfire 0

      Looks like perfectly good English to me.

  • Alejandro Sosa 0

    Hi David, As an old Visual FoxPro developer attempting a full dunk into .NET by converting, not migrating, a VFP application to .NET, I welcome the .NET 6 goal of more simplification and less ceremony. Keep it up. Anything that reduces the learning curve, like global using, is appreciated.

  • Reelix 0

    If you want developer driven upgrades, how about addressing the following .NET limitations 🙂

    1.) The native Zip class (System.IO.Compression.ZipArchive) does not support decompressing password-protected zip files – A third-party library is required.
    2.) The native FTP class (System.Net.FtpWebRequest) does not support listing file names/directories starting with a . on an FTP Server – A third-party library is required.
    3.) The native Active Directory / LDAP class (System.DirectoryServices.*) does not support connecting to a secured Windows LDAP Server from Linux – A third-party library is required.

    These are all standard features that seem to be missing from the base classes making them completely useless requiring the need of third-party substitutions 🙂

    • Tom Bruyneel 0

      You can always suggest them on github and gain traction for them to get them implemented 🙂

  • Ankit Vijay 0

    ArgumentNullException.ThrowIfNull(obj) is great, but getting ArgumentException.ThrowIfNullOrEmpty(stringObj) and ArgumentException.ThrowIfNullOrWhiteSpace(stringObj) would be an icing on the cake.

    • Andriy Savin 0

      I agree with you, but if you add parameter name to each of these, these checks will be too long to read. Even ArgumentNullException.ThrowIfNull is not good in this sense. I’d prefer to see something like EF team uses in their code: Check.NotNull or Check.NotEmpty.

  • Andriy Savin 0

    Hi, in the example with the timer, what would be the difference compared to using Task.Delay in that loop?

    • David FowlerMicrosoft employee 0

      It behaves a little differently
      – If your code runs longer than the period, then WaitForNextTickAsync will complete immediately
      – It’s more efficient as it’s single reusable timer and awaitable allocation vs a timer and task per call.

      • Andriy Savin 0


  • Jon Miller 0

    Regarding Process Path and Id. When are you going to add a way to access the process command-line arguments?

  • Jon Miller 0

    Glad there’s the new Chunk() method. I’ve been wanting that one for awhile and had to write my own which wasn’t very efficient.

Feedback usabilla icon