System.Linq.Async is part of .NET 10 - LINQ for IAsyncEnumerable

3/10/2025
2 minute read

IAsyncEnumerable is a type that was introduced in netcoreapp3.1 times. While somewhat an enumerable (even though async in nature), it never had the capabilities as its synchronous counterpart. Until dotnet 10! Now we have some feature-parity between those two.

AsyncEnumerable

AsyncEnumerable, just like its synchronous version Enumerable, defines all the nice extensions (like Select, Where and so on) for the IAsyncEnumerable type. So you can write code like this:

IAsyncEnumerable<MyObject> items = GetItems();
IReadOnlyCollection<MyObject> filtered = await items
  .Where(x => x.IsEnabled)
	.ToListAsync(cancellationToken: token);

Or another example:

IAsyncEnumerable<MyObject> items = GetItems();
var result = await items.Select(s => s.SomeNumber).AverageAsync(cancellationToken);

So the materializing bit has the Async suffix. All in all it might help with the adoption of IAsyncEnumerable as I don't see that type much often (also given that it is a bit niche in use anyway). If you want to checkout the original GitHub issue:https://github.com/dotnet/runtime/issues/79782

Or if you want to see the whole API-diff: https://github.com/dotnet/core/blob/main/release-notes/10.0/preview/preview1/api-diff/Microsoft.NETCore.App/10.0-preview1_System.Linq.md

System.Linq.Async - the source of the API

The API is not new. The original API comes from here: https://github.com/dotnet/reactive - It was shipped via the System.Linq.Async NuGet package and namespace. While the NuGet package is still valid for everything below dotnet 10, it doesn't make much sense to use it from dotnet 10 onwards. Therefore the package can be removed entirely. The API is identical as far as I can tell.

Follow the breaking change announcement, if you have a transitive dependency that uses System.Linq.Async you can exclude the library like this:

<PackageReference Include="System.Linq.Async" Version="6.0.1">
  <ExcludeAssets>all</ExcludeAssets>
</PackageReference>

What is IAsyncEnumerable?

For folks that might not have a grasp on what IAsyncEnumerable is and why you need it, imagine the following: You have a collection of something. You can represent this with IEnumerable<T>. You know this from things like Enumerable.Range(0, 10_000) which generated 10000 elements. But what if the generation of a single element is not synchronous? Let's take Task<IEnumerable<T>> - that surely transpires the message!? Well not really. You would await the generation of all elements at once, but not one-by-one.

Ok next try: What about IEnumerable<Task<T>>: We are getting warmer! The semantics would tell: Hey I have a collection where each element has to be awaited! But you would have to take care of each element yourself. So the method directly returns the enumeration of tasks and now it is up to you to do the right thing!

And this is where IAsyncEnumerable comes into play. From a mental model is is like IEnumerable<Task<T>> but gives you methods and tools to remove some of the handling to lead you easier into a "pit of success". And now with Linq.Async even more!

An error has occurred. This application may no longer respond until reloaded. Reload x