Neu in .NET 10.0 [26]: LINQ-Operationen auf IAsyncEnumerable<T>
LINQ-Operationen sind nun auch auf Mengen des Typs IAsyncEnumerable<T> möglich.
(Bild: Pincasso / Shutterstock.com)
- Dr. Holger Schwichtenberg
IAsyncEnumerable<T> ist die in .NET Core 3.0 eingeführte asynchrone Variante von IEnumerable<T>. Sie ermöglicht asynchrone Streams: das schrittweise, nicht-blockierende Iterieren über Daten, die asynchron bereitgestellt werden.
LINQ-Operationen auf IAsyncEnumerable<T> erforderten bisher das NuGet-Paket System.Linq.Async. Nun ist diese Funktionalität im Kern von .NET verfügbar.
(Bild:Â King / stock.adobe.com)
Das ist neu in .NET 11.0: Dr. Holger Schwichtenberg und weitere Experten präsentieren am 17. November 2026 auf der Online-Konferenz betterCode() .NET 11.0 die Änderungen für Entwicklerinnen und Entwickler in .NET SDK, C# 15.0 und mehr. Bis zur Veröffentlichung des Programms sind vergünstigte Blind-Bird-Tickets verfügbar.
Folgender Code nutzt Where() und OrderBy() mit IAsyncEnumerable<T>:
namespace NET10_Console.FCL;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
public readonly record struct Kabinettsmitglied(string Name, string Amt);
/// <summary>
/// LINQ auf IAsyncEnumerable<T> erforderte bisher NuGet-Paket System.Linq.Async
/// </summary>
public class FCL10_LINQAsyncEnumerable
{
public async Task Run()
{
CUI.Demo(nameof(FCL10_LINQAsyncEnumerable));
// Hole Personen im Bundeskabinett
var datenquelle = new Bundeskabinett().GetAll();
// Asynchrone Iteration mit LINQ (Filtern und optional Sortieren)
// Achtung: .OrderBy(x=>x.Name) führt dazu, dass foreach auf das letzte Element warten muss --> Vorteil von async entfällt :-(
await foreach (var person in datenquelle.Where(x => x.Amt.Contains("ministerin")))
{
Console.WriteLine(DateTime.Now.ToLongTimeString() + ": " + person);
}
}
public class Bundeskabinett
{
/// <summary>
/// Liefert alle Mitglieder des Bundeskabinetts (Stand: 21.09.2025) als Async-Stream.
/// Quelle: Bundesregierung.de (siehe Code-Kommentar).
/// </summary>
public async IAsyncEnumerable<Kabinettsmitglied> GetAll(
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
// Quelle: https://www.bundesregierung.de/breg-de/bundesregierung/bundeskabinett
var daten = new List<Kabinettsmitglied>
{
new("Friedrich Merz", "Bundeskanzler"),
new("Lars Klingbeil", "Bundesminister der Finanzen"),
new("Alexander Dobrindt", "Bundesminister des Innern"),
new("Dr. Johann Wadephul", "Bundesminister des Auswärtigen"),
new("Boris Pistorius", "Bundesminister der Verteidigung"),
new("Katherina Reiche", "Bundesministerin fĂĽr Wirtschaft und Energie"),
new("Dorothee Bär", "Bundesministerin für Forschung, Technologie und Raumfahrt"),
new("Dr. Stefanie Hubig", "Bundesministerin der Justiz und fĂĽr Verbraucherschutz"),
new("Karin Prien", "Bundesministerin fĂĽr Bildung, Familie, Senioren, Frauen und Jugend"),
new("Bärbel Bas", "Bundesministerin für Arbeit und Soziales"),
new("Dr. Karsten Wildberger", "Bundesminister fĂĽr Digitales und Staatsmodernisierung"),
new("Patrick Schnieder", "Bundesminister fĂĽr Verkehr"),
new("Carsten Schneider", "Bundesminister fĂĽr Umwelt, Klimaschutz, Naturschutz und nukleare Sicherheit"),
new("Nina Warken", "Bundesministerin fĂĽr Gesundheit"),
new("Alois Rainer", "Bundesminister für Landwirtschaft, Ernährung und Heimat"),
new("Reem Alabali Radovan", "Bundesministerin fĂĽr wirtschaftliche Zusammenarbeit und Entwicklung"),
new("Verena Hubertz", "Bundesministerin fĂĽr Wohnen, Stadtentwicklung und Bauwesen"),
new("Thorsten Frei", "Bundesminister fĂĽr besondere Aufgaben / Chef des Bundeskanzleramtes"),
};
foreach (var m in daten)
{
cancellationToken.ThrowIfCancellationRequested();
yield return m;
await Task.Delay(200); // Simuliere etwas Wartezeit
await Task.Yield(); // sorgt für echte Asynchronität beim Streamen
}
}
}
}
Videos by heise
Ein Praxisbeispiel dazu: Folgender Code zeigt die Ausführung einer Reihe von HTTP-Aufrufen mit asynchroner Ausgabe der Ergebnisse via Task.WhenEach(). Hier ist nun ein Filtern mit Where() oder eine Sortierung mit OrderBy() möglich. Diese blockiert jedoch – wie üblich – die Weiterverarbeitung, bis das letzte Ergebnis vorliegt, und beeinträchtigt damit den Vorteil der asynchronen Ausführung.
public async Task Run()
{
CUI.Demo(nameof(FCL10_LINQAsyncEnumerable));
Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: Starte Websiteabfragen...");
using HttpClient http = new();
Task<HttpResponseMessage> t1 = http.GetAsync("https://www.CNN.com");
Task<HttpResponseMessage> t2 = http.GetAsync("https://microsoft.com");
Task<HttpResponseMessage> t3 = http.GetAsync("https://www.dotnetframework.de");
Task<HttpResponseMessage> t4 = http.GetAsync("https://www.dotnet-lexikon.de");
Task<HttpResponseMessage> t5 = http.GetAsync("https://www.dotnet9.de");
Task<HttpResponseMessage> t6 = http.GetAsync("https://www.dotnet10.de");
Task<HttpResponseMessage> t7 = http.GetAsync("https://www.web.de");
// Liste aus Tasks
List<Task<HttpResponseMessage>> taskList = new() { t1, t2, t3, t4, t5, t6, t7 };
// WhenEach() gibt es seit .NET 9.0
IAsyncEnumerable<Task<HttpResponseMessage>> taskList2 = Task.WhenEach(taskList);
// NEU in .NET 10.0: LINQ auf IAsyncEnumerable<T>
taskList2 = taskList2.Where(x => x.Result.RequestMessage.RequestUri.ToString().Contains("dot"));
taskList2 = taskList2.OrderBy(x => x.Result.RequestMessage.RequestUri.Host);
await foreach (Task<HttpResponseMessage> t in taskList2)
{
try
{
Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: {t.Status} {t.Result?.RequestMessage?.RequestUri} = {t?.Result?.StatusCode}"); // Status des HTTP-Aufrufs
}
catch (Exception ex)
{
CUI.Warning(ex);
}
}
}
(rme)