From 4d6695023f055bd0c4a45254afd94cada1f99cd2 Mon Sep 17 00:00:00 2001 From: wuyangfan <1102042793@qq.com> Date: Sun, 17 May 2026 23:51:59 +0800 Subject: [PATCH] feat: add Query overload for CommandDefinition + Type[] Expose synchronous multi-map queries with arbitrary type count via CommandDefinition, matching the existing MultiMapImpl path. --- Dapper/PublicAPI.Unshipped.txt | 3 +- Dapper/SqlMapper.cs | 18 ++++++++++- tests/Dapper.Tests/MultiMapTests.cs | 46 +++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/Dapper/PublicAPI.Unshipped.txt b/Dapper/PublicAPI.Unshipped.txt index cf9343ccf..09f22637a 100644 --- a/Dapper/PublicAPI.Unshipped.txt +++ b/Dapper/PublicAPI.Unshipped.txt @@ -1,3 +1,4 @@ #nullable enable static Dapper.SqlMapper.Settings.PreferTypeHandlersForEnums.get -> bool -static Dapper.SqlMapper.Settings.PreferTypeHandlersForEnums.set -> void \ No newline at end of file +static Dapper.SqlMapper.Settings.PreferTypeHandlersForEnums.set -> void +static Dapper.SqlMapper.Query(this System.Data.IDbConnection! cnn, Dapper.CommandDefinition command, System.Type![]! types, System.Func! map, string! splitOn = "Id") -> System.Collections.Generic.IEnumerable! \ No newline at end of file diff --git a/Dapper/SqlMapper.cs b/Dapper/SqlMapper.cs index 2fa0e72b7..fcf945026 100644 --- a/Dapper/SqlMapper.cs +++ b/Dapper/SqlMapper.cs @@ -1230,7 +1230,6 @@ private static IEnumerable QueryImpl(this IDbConnection cnn, CommandDefini } var func = tuple.Func; - var convertToType = Nullable.GetUnderlyingType(effectiveType) ?? effectiveType; while (reader.Read()) { object? val = func(reader); @@ -1549,6 +1548,23 @@ public static IEnumerable Query Query(this IDbConnection cnn, string sql, Func map, object? param = null, IDbTransaction? transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null) => MultiMap(cnn, sql, map, param, transaction, buffered, splitOn, commandTimeout, commandType); + /// + /// Perform a multi-mapping query with an arbitrary number of input types. + /// This returns a single type, combined from the raw types via . + /// + /// The combined type to return. + /// The connection to query on. + /// The command used to execute the query. + /// Array of types in the recordset. + /// The function to map row types to the return type. + /// The field we should split and read the second object from (default: "Id"). + /// An enumerable of . + public static IEnumerable Query(this IDbConnection cnn, CommandDefinition command, Type[] types, Func map, string splitOn = "Id") + { + var results = MultiMapImpl(cnn, command, types, map, splitOn, null, null, true); + return command.Buffered ? results.ToList() : results; + } + /// /// Perform a multi-mapping query with an arbitrary number of input types. /// This returns a single type, combined from the raw types via . diff --git a/tests/Dapper.Tests/MultiMapTests.cs b/tests/Dapper.Tests/MultiMapTests.cs index 510860e84..d3e46efee 100644 --- a/tests/Dapper.Tests/MultiMapTests.cs +++ b/tests/Dapper.Tests/MultiMapTests.cs @@ -490,6 +490,52 @@ public void TestMultiMapArbitraryMaps() } } + [Fact] + public void TestMultiMapArbitraryMapsCommandDefinition() + { + 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) +"; + connection.Execute(createSql); + 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 mapper = objects => + { + var board = (ReviewBoard)objects[0]; + board.User9 = (User)objects[9]; + return board; + }; + + var command = new CommandDefinition(sql); + var data = connection.Query(command, types, mapper).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 void TestMultiMapGridReader() {