Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Dapper/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#nullable enable
static Dapper.SqlMapper.Settings.PreferTypeHandlersForEnums.get -> bool
static Dapper.SqlMapper.Settings.PreferTypeHandlersForEnums.set -> void
static Dapper.SqlMapper.Settings.PreferTypeHandlersForEnums.set -> void
static Dapper.SqlMapper.QueryAsync<TReturn>(this System.Data.IDbConnection! cnn, Dapper.CommandDefinition command, System.Type![]! types, System.Func<object![]!, TReturn>! map, string! splitOn = "Id") -> System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<TReturn>!>!
21 changes: 18 additions & 3 deletions Dapper/SqlMapper.Async.cs
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,6 @@ private static async Task<IEnumerable<T>> QueryAsync<T>(this IDbConnection cnn,
if (command.Buffered)
{
var buffer = new List<T>();
var convertToType = Nullable.GetUnderlyingType(effectiveType) ?? effectiveType;
while (await reader.ReadAsync(cancel).ConfigureAwait(false))
{
object val = func(reader);
Expand Down Expand Up @@ -949,6 +948,20 @@ private static async Task<IEnumerable<TReturn>> MultiMapAsync<TFirst, TSecond, T
}
}

/// <summary>
/// Perform an asynchronous multi-mapping query with an arbitrary number of input types.
/// This returns a single type, combined from the raw types via <paramref name="map"/>.
/// </summary>
/// <typeparam name="TReturn">The combined type to return.</typeparam>
/// <param name="cnn">The connection to query on.</param>
/// <param name="command">The command used to execute the query.</param>
/// <param name="types">Array of types in the recordset.</param>
/// <param name="map">The function to map row types to the return type.</param>
/// <param name="splitOn">The field we should split and read the second object from (default: "Id").</param>
/// <returns>An enumerable of <typeparamref name="TReturn"/>.</returns>
public static Task<IEnumerable<TReturn>> QueryAsync<TReturn>(this IDbConnection cnn, CommandDefinition command, Type[] types, Func<object[], TReturn> map, string splitOn = "Id") =>
MultiMapAsync(cnn, command, types, map, splitOn);

/// <summary>
/// Perform an asynchronous multi-mapping query with an arbitrary number of input types.
/// This returns a single type, combined from the raw types via <paramref name="map"/>.
Expand Down Expand Up @@ -1301,6 +1314,7 @@ static async IAsyncEnumerable<T> Impl(IDbConnection cnn, Type effectiveType, Com
bool wasClosed = cnn.State == ConnectionState.Closed;
using var cmd = command.TrySetupAsyncCommand(cnn, info.ParamReader);
DbDataReader? reader = null;
bool readerDrained = false;
try
{
if (wasClosed) await cnn.TryOpenAsync(cancel).ConfigureAwait(false);
Expand All @@ -1312,6 +1326,7 @@ static async IAsyncEnumerable<T> Impl(IDbConnection cnn, Type effectiveType, Com
{
if (reader.FieldCount == 0)
{
readerDrained = true;
yield break;
}
tuple = info.Deserializer = new DeserializerState(hash, GetDeserializer(effectiveType, reader, 0, -1, false));
Expand All @@ -1320,20 +1335,20 @@ static async IAsyncEnumerable<T> Impl(IDbConnection cnn, Type effectiveType, Com

var func = tuple.Func;

var convertToType = Nullable.GetUnderlyingType(effectiveType) ?? effectiveType;
while (await reader.ReadAsync(cancel).ConfigureAwait(false))
{
object val = func(reader);
yield return GetValue<T>(reader, effectiveType, val);
}
while (await reader.NextResultAsync(cancel).ConfigureAwait(false)) { /* ignore subsequent result sets */ }
readerDrained = true;
command.OnCompleted();
}
finally
{
if (reader is not null)
{
if (!reader.IsClosed)
if (!readerDrained && !reader.IsClosed)
{
try { cmd?.Cancel(); }
catch { /* don't spoil any existing exception */ }
Expand Down
47 changes: 47 additions & 0 deletions tests/Dapper.Tests/AsyncTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,53 @@ public async Task TestMultiMapArbitraryMapsAsync()
}
}

[Fact]
public async Task TestMultiMapArbitraryMapsCommandDefinitionAsync()
{
const string createSql = @"
create table #ReviewBoards (Id int, Name varchar(20), User1Id int, User2Id int, User3Id int, User4Id int, User5Id int, User6Id int, User7Id int, User8Id int, User9Id int)
create table #Users (Id int, Name varchar(20))

insert #Users values(9, 'User 9')
insert #ReviewBoards values(1, 'Review Board 1', 9, 9, 9, 9, 9, 9, 9, 9, 9)
";
await connection.ExecuteAsync(createSql).ConfigureAwait(false);
try
{
const string sql = @"
select rb.Id, rb.Name, u1.*, u2.*, u3.*, u4.*, u5.*, u6.*, u7.*, u8.*, u9.*
from #ReviewBoards rb
inner join #Users u1 on u1.Id = rb.User1Id
inner join #Users u2 on u2.Id = rb.User2Id
inner join #Users u3 on u3.Id = rb.User3Id
inner join #Users u4 on u4.Id = rb.User4Id
inner join #Users u5 on u5.Id = rb.User5Id
inner join #Users u6 on u6.Id = rb.User6Id
inner join #Users u7 on u7.Id = rb.User7Id
inner join #Users u8 on u8.Id = rb.User8Id
inner join #Users u9 on u9.Id = rb.User9Id";

var types = new[] { typeof(ReviewBoard), typeof(User), typeof(User), typeof(User), typeof(User), typeof(User), typeof(User), typeof(User), typeof(User), typeof(User) };
Func<object[], ReviewBoard> mapper = objects =>
{
var board = (ReviewBoard)objects[0];
board.User9 = (User)objects[9];
return board;
};

using var cts = new CancellationTokenSource();
var command = new CommandDefinition(sql, cancellationToken: cts.Token);
var data = (await connection.QueryAsync<ReviewBoard>(command, types, mapper).ConfigureAwait(false)).ToList();

Assert.Equal(1, data.Count);
Assert.Equal("User 9", data[0].User9!.Name);
}
finally
{
connection.Execute("drop table #Users drop table #ReviewBoards");
}
}

[Fact]
public async Task Issue157_ClosedReaderAsync()
{
Expand Down