New .NET 6 APIs driven by the developer community
.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);
Console.WriteLine(length);
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;
Console.WriteLine(pid);
Console.WriteLine(path);
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);
Parallel.ForEachAsync
We finally added Parallel.ForEachAsync, a way to schedule asynchronous work that allows you to control the degree of parallelism:
var urlsToDownload = new []
{
"https://dotnet.microsoft.com",
"https://www.microsoft.com",
"https://twitter.com/davidfowl"
};
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
configuration.GetRequiredSection("MyOptions").Bind(options);
class MyOptions
{
public string? SettingValue { get; set;}
}
LINQ
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)
{
Console.WriteLine(item);
}
}
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);
}
Console.WriteLine(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));
ThrowIfNull
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)
{
ArgumentNullException.ThrowIfNull(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;
unsafe
{
byte* buffer = (byte*)NativeMemory.Alloc(100);
NativeMemory.Free(buffer);
}
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");
tcs.TrySetResult();
});
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}");
});
listener.Start();
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())
{
Console.WriteLine(DateTime.UtcNow);
}
Summary
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.
59 comments
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.
Regarding Process Path and Id. When are you going to add a way to access the process command-line arguments?
That method has been there forever https://docs.microsoft.com/en-us/dotnet/api/system.environment.getcommandlineargs?view=net-5.0.
Hi, in the example with the timer, what would be the difference compared to using Task.Delay in that loop?
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.
Thanks!
ArgumentNullException.ThrowIfNull(obj)
is great, but gettingArgumentException.ThrowIfNullOrEmpty(stringObj)
andArgumentException.ThrowIfNullOrWhiteSpace(stringObj)
would be an icing on the cake.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.
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 🙂
You can always suggest them on github and gain traction for them to get them implemented 🙂
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.
I think some of the XML doc comments could do with a proof-reader! 🙂
For example: Chunk
Looks like perfectly good English to me.
Is ThrowIfNull any different from the C# 9 simplified parameter null validation?
The language feature has been delayed again and won’t ship with C# 10.
Once implemented, the language feature will likely generate calls to
ThrowIfNull
. Calling it explicitly will give you more control then (For example whennull
is acceptable in some states).Had the same thought, when I read the title
Modern Timer API
but still, why didn’t you mark the “old” timers obsolete?May I ask why you phrased it as “We added..”, IMHO “We” seems to imply that Microsoft Team did it, which seems condraditing the first paragrah that wanted to highlight the community driven APIs.
The greater “we”. “We the .NET people”. On a more serious note, APIs that are suggested by the community aren’t always implemented by the community.
In the section ‘Reading & Writing Files’ I totally miss what’s helpful in this mess. Where is “reading & writing”?? How it helps me above existing “File.WriteAllBytes”?
Random access – it was existing before, what new functions are introduced and how they improve old code? Any sample?
See the original proposal here
In short, it enables scenarios that need the highest performance, lowest allocation thread safe file access. For most people, the existing API remain good choices.