When data arrives gradually and you don't want to load it all into memory: exports, large queries, message consumers, paging through an external API.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | // ====================================================================== // Q: IAsyncEnumerable and await foreach: when and how? // // When data arrives gradually and you don't want to load it all into memory: // exports, large queries, message consumers, paging through an external API. // - [EnumeratorCancellation] is required for .WithCancellation(ct) to work. // - An IAsyncEnumerable from EF Core keeps the connection open while you read. // Don't make slow calls inside the loop. // - In ASP.NET Core, an action that returns IAsyncEnumerable<T> streams JSON // (demo: GET /api/orders/export). // ====================================================================== public async IAsyncEnumerable<ProductDto> GetAllFromPimAsync( [EnumeratorCancellation] CancellationToken ct = default) { string? cursor = null; do { var page = await pim.GetProductsAsync(cursor, ct); foreach (var p in page.Items) yield return p; // the consumer gets the first items before all of them arrive cursor = page.NextCursor; } while (cursor is not null); } await foreach (var p in GetAllFromPimAsync().WithCancellation(ct)) { /* ... */ } |