Fix MultiMapAsync disposing the reader before unbuffered results are enumerated - #2229
Open
jinseojang0903 wants to merge 1 commit into
Open
jinseojang0903 wants to merge 1 commit into
jinseojang0903 wants to merge 1 commit into
Conversation
…enumerated Both the fixed-arity and Type[]-based MultiMapAsync overloads wrapped the DbDataReader in a `using` that disposed it as soon as the async method returned. For buffered:false calls the returned IEnumerable<TReturn> is a lazy (yield-based) sequence that hasn't started executing yet, so the reader was already closed by the time the caller enumerated it, throwing ObjectDisposedException / "reader is closed" on first access. Mirrors the disposal-deferral pattern already used by the single-type QueryAsync<T> unbuffered path: transfer reader ownership into the returned sequence instead of disposing it unconditionally. Fixes DapperLib#2099 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPzJgxak5nZ4u3j88ui5xX
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.
Fixes #2099.
Problem
When calling any multi-map
QueryAsyncoverload (e.g.QueryAsync<TFirst, TSecond, TReturn>or theType[]-basedQueryAsync<TReturn>)with
buffered: false, enumerating the returned sequence throws once the reader has already been disposed:System.InvalidOperationException: Invalid attempt to call FieldCount when reader is closed.
at Dapper.SqlMapper.GetColumnHash(...)
at Dapper.SqlMapper.MultiMapImpl[...]+MoveNext()
Root cause
Both
MultiMapAsync<TFirst,...,TSeventh,TReturn>andMultiMapAsync<TReturn>(Type[] overload) inSqlMapper.Async.cswrapped theDbDataReaderin ausingblock.MultiMapImplbuilds its result as a lazy,yield return-basedIEnumerable<TReturn>that doesn'tstart executing until the caller enumerates it. For
buffered: false, the async method returns that un-enumerated sequence directly — sothe
usingdisposes the reader immediately on return, before the caller ever gets a chance to read from it.The single-type
QueryAsync<T>path already avoids this by deferring disposal: it hands the reader into a small iterator(
ExecuteReaderSync) that only disposes it once the caller finishes enumerating. This PR applies the same pattern to both multi-mapoverloads, adding a matching
IEnumerable<TReturn>-based overload ofExecuteReaderSyncfor them to share.Fix
SqlMapper.Async.cs: transfer reader ownership into the deferred sequence instead of disposing it unconditionally whenbuffered: false.buffered: truepath (already worked correctly, since.ToList()fully consumes the reader before the method returns).Tests
Added two regression tests in
AsyncTests.cs, following the existingTestMultiMapWithSplitAsync/TestMultiMapArbitraryWithSplitAsyncstyle:
TestMultiMapWithSplitUnbufferedAsyncTestMultiMapArbitraryWithSplitUnbufferedAsyncBoth fail with the reported exception before this fix and pass after it. Full suite run locally (net8.0 + net10.0, SQL
Server/MySQL/Postgres) with no regressions.