IAsyncEnumerable, and the streaming query

Returning a large result set meant materialising the whole thing into a list, and the async equivalent of a generator did not exist until C# 8.

public async IAsyncEnumerable<Order> StreamAsync(
    [EnumeratorCancellation] CancellationToken ct = default)
{
    await using var reader = await _command.ExecuteReaderAsync(ct);

    while (await reader.ReadAsync(ct))
    {
        yield return Map(reader);
    }
}

// await foreach (var order in repo.StreamAsync(ct)) { ... }

This is the same idea as a PHP generator with the addition that each step can await, which is what a database reader needs. The EnumeratorCancellation attribute is required for the cancellation token to reach the enumerator and its absence is a silent no-op — the token is accepted and ignored. await foreach is the consumer syntax and it disposes the enumerator correctly, which a manual loop frequently does not.