0Pricing
C# Academy · Lektion

IAsyncEnumerable und await foreach

Streamen Sie Daten asynchron mit IAsyncEnumerable, verwenden Sie yield in asynchronen Iteratoren und konsumieren Sie Daten mit await foreach.

IAsyncEnumerable und await foreach ist eine kostenlose C# Academy-Lektion auf CoddyKit. Dies ist Lektion 1 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des C# Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der C# Academy-Kurs umfasst insgesamt 4 Lektionen.

Warum IAsyncEnumerable?

IAsyncEnumerable<T> (eingeführt in C# 8 und .NET Core 3) ermöglicht asynchrones Streaming — Sie geben Elemente einzeln zurück, sobald sie verfügbar sind, ohne zuvor alles in den Speicher zu laden. Denken Sie etwa an das zeilenweise Lesen eines Datenbankcursors oder das Streamen von API-Antworten.

Einen asynchronen Iterator schreiben

Deklarieren Sie eine Methode mit dem Rückgabetyp IAsyncEnumerable<T> und verwenden Sie yield return innerhalb einer asynchronen Methode. Die Schlüsselwörter await und yield return lassen sich problemlos gemeinsam verwenden.

public async IAsyncEnumerable<int> GenerateNumbersAsync(int count)
{
    for (int i = 0; i < count; i++)
    {
        await Task.Delay(100); // simulate async work per item
        yield return i;
    }
}

Mit await foreach konsumieren

Konsumieren Sie ein IAsyncEnumerable<T> mit await foreach. Jede Iteration wartet asynchron auf das nächste Element — Konsument und Produzent werden dadurch auf natürliche Weise aufeinander abgestimmt.

await foreach (var number in GenerateNumbersAsync(10))
{
    Console.WriteLine(number);
}

// With cancellation:
var cts = new CancellationTokenSource();
await foreach (var n in GenerateNumbersAsync(100)
    .WithCancellation(cts.Token))
{
    if (n > 50) { cts.Cancel(); break; }
    Console.WriteLine(n);
}

Aus einer Datenbank streamen

EF Core unterstützt das Streamen von Abfrageergebnissen mit AsAsyncEnumerable(). Dadurch werden die Zeilen einzeln gelesen, ohne die gesamte Ergebnismenge in den Speicher zu laden — bei großen Tabellen ist das entscheidend.

public async IAsyncEnumerable<Product> StreamProductsAsync(
    [EnumeratorCancellation] CancellationToken ct = default)
{
    await foreach (var product in _db.Products
        .Where(p => p.IsActive)
        .AsAsyncEnumerable()
        .WithCancellation(ct))
    {
        yield return product;
    }
}

HTTP-Antworten streamen

Streamen Sie große Antworten an Clients, ohne die gesamte Nutzlast zwischenzuspeichern. Der Client empfängt die Daten schrittweise, während der Server die Verarbeitung fortsetzt.

app.MapGet("/stream/products", (ProductRepository repo) =>
{
    // Returns an IAsyncEnumerable — ASP.NET Core streams it as JSON array
    return repo.StreamAllAsync();
});

// Or with custom serialization:
app.MapGet("/stream/events", async (HttpResponse response, EventStore store) =>
{
    response.ContentType = "application/json";
    await foreach (var e in store.StreamAsync())
    {
        await response.WriteAsJsonAsync(e);
        await response.Body.FlushAsync();
    }
});

Das EnumeratorCancellation-Attribut

Wenden Sie [EnumeratorCancellation] auf den CancellationToken-Parameter eines asynchronen Iterators an, damit WithCancellation() auf der Konsumentenseite das Token an den Produzenten weitergibt.

public async IAsyncEnumerable<LogEntry> TailLogAsync(
    string logFile,
    [EnumeratorCancellation] CancellationToken ct = default)
{
    while (!ct.IsCancellationRequested)
    {
        var newLines = await ReadNewLinesAsync(logFile, ct);
        foreach (var line in newLines)
            yield return new LogEntry(line);
        await Task.Delay(500, ct);
    }
}

// Consumer:
await foreach (var entry in TailLogAsync("/var/log/app.log")
    .WithCancellation(cts.Token))
{
    Console.WriteLine(entry.Message);
}

LINQ über IAsyncEnumerable

Das NuGet-Paket System.Linq.Async stellt asynchrone LINQ-Operatoren (WhereAwait, SelectAwait, ToListAsync) für IAsyncEnumerable bereit.

// dotnet add package System.Linq.Async

var expensiveProducts = await repo.StreamAllAsync()
    .WhereAwait(async p => await pricing.IsExpensiveAsync(p.Id))
    .SelectAwait(async p => await enricher.EnrichAsync(p))
    .Take(10)
    .ToListAsync();

// Or use built-in Where/Select that don't need async:
var names = repo.StreamAllAsync()
    .Where(p => p.IsActive)
    .Select(p => p.Name);

Mehrere asynchrone Streams zusammenführen

Um mehrere asynchrone Streams gleichzeitig zu verarbeiten, verwenden Sie Channel als Fan-in-Punkt — mehrere Produzenten schreiben hinein, und ein einzelner Konsument liest daraus.

async IAsyncEnumerable<T> Merge<T>(
    params IAsyncEnumerable<T>[] streams)
{
    var channel = Channel.CreateUnbounded<T>();

    var producers = streams.Select(async stream =>
    {
        await foreach (var item in stream)
            await channel.Writer.WriteAsync(item);
    });

    _ = Task.WhenAll(producers)
            .ContinueWith(_ => channel.Writer.Complete());

    await foreach (var item in channel.Reader.ReadAllAsync())
        yield return item;
}

Performance: Puffern vs. Streamen

Streamen Sie große Datenmengen grundsätzlich. Wenn Sie zunächst alles in eine Liste laden (ToListAsync()), befinden sich alle Daten gleichzeitig im Speicher — beim Streaming wird jeweils nur ein Element verarbeitet.

// BAD: loads all 1M records into memory
var allOrders = await _db.Orders.ToListAsync();
foreach (var order in allOrders)
    await ProcessAsync(order);

// GOOD: processes one record at a time
await foreach (var order in _db.Orders.AsAsyncEnumerable())
    await ProcessAsync(order);
// Memory usage: O(1) vs O(n)

Fehlerbehandlung in asynchronen Iteratoren

Umschließen Sie das gesamte await foreach mit try/catch, um Fehler des Produzenten zu behandeln. Fehler im Iterator werden an den Konsumenten an der aktuellen Iterationsstelle weitergegeben.

try
{
    await foreach (var item in RiskyStreamAsync())
    {
        await ProcessAsync(item);
    }
}
catch (HttpRequestException ex)
{
    Console.Error.WriteLine($"Stream error: {ex.Message}");
}
catch (OperationCanceledException)
{
    Console.WriteLine("Stream cancelled.");
}

Kurzer Test

Welchen wesentlichen Speichervorteil bietet IAsyncEnumerable gegenüber der Rückgabe einer List<T>?

Zusammenfassung: IAsyncEnumerable & await foreach

Wichtigste Erkenntnisse:

  • IAsyncEnumerable<T>: Elemente asynchron und einzeln streamen
  • yield return in asynchronen Methoden erzeugt asynchrone Iteratoren
  • await foreach: den Stream mit Abbruch über .WithCancellation() konsumieren
  • [EnumeratorCancellation]: gibt das Token des Konsumenten an den Iterator weiter
  • EF Core: Verwenden Sie AsAsyncEnumerable() für speichereffiziente Abfragen großer Datenmengen
  • Große Datenmengen verarbeiten: streamen statt ToListAsync()

Häufig gestellte Fragen

Ist die Lektion „IAsyncEnumerable und await foreach“ kostenlos?

Ja — der vollständige Text von „IAsyncEnumerable und await foreach“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des C# Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der C# Academy-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „IAsyncEnumerable und await foreach“?

Streamen Sie Daten asynchron mit IAsyncEnumerable, verwenden Sie yield in asynchronen Iteratoren und konsumieren Sie Daten mit await foreach. Du übst C# Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um C# Academy zu starten?

Keine Vorkenntnisse erforderlich. C# Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 1 von 4.

Wie lange dauert die Lektion „IAsyncEnumerable und await foreach“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser C# Academy-Lektion Code schreiben und ausführen?

Ja. Jede C# Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. IAsyncEnumerable und await foreach
  2. System.Threading.Channels
  3. ValueTask und Vermeidung von Allokationen
  4. ConfigureAwait und SynchronizationContext
← Zurück zu C# Academy