Use QueryUnbufferedAsync for streaming IAsyncEnumerable results#671
Open
Use QueryUnbufferedAsync for streaming IAsyncEnumerable results#671
Conversation
Co-authored-by: sebastienros <1165805+sebastienros@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Use QueryUnbufferedAsync in DefaultQuery ToAsyncEnumerable
Use QueryUnbufferedAsync for streaming IAsyncEnumerable results
Nov 17, 2025
| { | ||
| // TODO: [IAsyncEnumerable] Once Dapper supports IAsyncEnumerable we can replace this call by a non-buffered one | ||
| foreach (var item in await ListImpl(cancellationToken)) | ||
| await foreach (var item in ListImplAsync(cancellationToken)) |
| @@ -1325,17 +1326,14 @@ Task<IEnumerable<T>> IQuery<T>.ListAsync(CancellationToken cancellationToken) | |||
| async IAsyncEnumerable<T> IQuery<T>.ToAsyncEnumerable(CancellationToken cancellationToken) | |||
| #pragma warning restore CS8425 // Async-iterator member has one or more parameters of type 'CancellationToken' but none of them is decorated with the 'EnumeratorCancellation' attribute, so the cancellation token parameter from the generated 'IAsyncEnumerable<>.GetAsyncEnumerator' will be unconsumed | |||
Contributor
Author
There was a problem hiding this comment.
Already fixed in commit ea9a596! The pragma was removed by simplifying the implementation - ToAsyncEnumerable now directly returns ListImpl instead of being an async iterator.
| } | ||
| } | ||
|
|
||
| internal async IAsyncEnumerable<T> ListImplAsync([EnumeratorCancellation] CancellationToken cancellationToken) |
Owner
There was a problem hiding this comment.
@copilot why does it need to be a new method? What about replacing the existing one?
Contributor
Author
There was a problem hiding this comment.
Good suggestion! I've replaced ListImpl to return IAsyncEnumerable<T> and updated ListAsync to buffer the results. This eliminates the duplicate method and follows the TODO comment's guidance. See commit ea9a596.
| @@ -1325,17 +1326,14 @@ Task<IEnumerable<T>> IQuery<T>.ListAsync(CancellationToken cancellationToken) | |||
| async IAsyncEnumerable<T> IQuery<T>.ToAsyncEnumerable(CancellationToken cancellationToken) | |||
| #pragma warning restore CS8425 // Async-iterator member has one or more parameters of type 'CancellationToken' but none of them is decorated with the 'EnumeratorCancellation' attribute, so the cancellation token parameter from the generated 'IAsyncEnumerable<>.GetAsyncEnumerator' will be unconsumed | |||
- ToAsyncEnumerable now directly returns ListImpl result - Removed pragma warning by eliminating unnecessary wrapper - ListImpl now returns IAsyncEnumerable<T> instead of separate ListImplAsync method - ListAsync buffers results from the async enumerable - Applied same pattern to QueryIndex class Co-authored-by: sebastienros <1165805+sebastienros@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Dapper added
QueryUnbufferedAsyncreturningIAsyncEnumerable<T>in version 2.1+, eliminating the need to buffer results when usingToAsyncEnumerable().Changes
ListImpl: Changed to returnIAsyncEnumerable<T>using Dapper'sQueryUnbufferedAsyncto stream results directly from the databaseToAsyncEnumerable: Now directly returnsListImplfor true streaming (no wrapper needed)ListAsync: Buffers results from the async enumerable into aList<T>Query<T>andQueryIndex<T>: Same pattern applied consistently across both implementationsBefore/After
Before:
After:
The new implementation streams results row-by-row using
connection.QueryUnbufferedAsync<T>().WithCancellation(cancellationToken), reducing memory footprint for large result sets. This eliminates code duplication and follows the original TODO guidance to "return it by default, and buffer it in ListAsync instead."Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.