From 55297ab63d2066d58f93c8d3eb348c63eb2bcbfd Mon Sep 17 00:00:00 2001 From: "Dailey, Alaric" Date: Sat, 24 Oct 2026 07:37:40 -0500 Subject: [PATCH] Use type handlers for list expansion parameters --- Dapper/SqlMapper.cs | 24 ++++++--- Directory.Packages.props | 1 + tests/Dapper.Tests/Dapper.Tests.csproj | 5 +- tests/Dapper.Tests/FakeDbTests.Issue1649.cs | 58 +++++++++++++++++++++ 4 files changed, 79 insertions(+), 9 deletions(-) create mode 100644 tests/Dapper.Tests/FakeDbTests.Issue1649.cs diff --git a/Dapper/SqlMapper.cs b/Dapper/SqlMapper.cs index 8f3956cab..c5f0df2ad 100644 --- a/Dapper/SqlMapper.cs +++ b/Dapper/SqlMapper.cs @@ -2180,9 +2180,10 @@ public static void PackListParameters(IDbCommand command, string namePrefix, obj bool byPosition = ShouldPassByPosition(command.CommandText); var list = value as IEnumerable; var count = 0; - bool isString = value is IEnumerable; - bool isDbString = value is IEnumerable; - DbType? dbType = null; + bool isString = value is IEnumerable; + bool isDbString = value is IEnumerable; + DbType? dbType = null; + ITypeHandler? handler = null; int splitAt = SqlMapper.Settings.InListStringSplitCount; bool viaSplit = splitAt >= 0 @@ -2199,9 +2200,9 @@ public static void PackListParameters(IDbCommand command, string namePrefix, obj { throw new NotSupportedException("The first item in a list-expansion cannot be null"); } - if (!isDbString) - { - dbType = LookupDbType(item.GetType(), "", true, out var handler); + if (!isDbString) + { + dbType = LookupDbType(item.GetType(), "", true, out handler); } } var nextName = namePrefix + count.ToString(); @@ -2222,7 +2223,16 @@ public static void PackListParameters(IDbCommand command, string namePrefix, obj } } - var tmp = listParam.Value = SanitizeParameterValue(item); + object? tmp; + if (handler is null) + { + tmp = listParam.Value = SanitizeParameterValue(item); + } + else + { + handler.SetValue(listParam, item!); + tmp = listParam.Value; + } if (tmp is not null && tmp is not DBNull) lastValue = tmp; // only interested in non-trivial values for padding diff --git a/Directory.Packages.props b/Directory.Packages.props index 2f91a3cca..8f2772419 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -35,6 +35,7 @@ + diff --git a/tests/Dapper.Tests/Dapper.Tests.csproj b/tests/Dapper.Tests/Dapper.Tests.csproj index 93b498618..09947a89a 100644 --- a/tests/Dapper.Tests/Dapper.Tests.csproj +++ b/tests/Dapper.Tests/Dapper.Tests.csproj @@ -31,8 +31,9 @@ - - + + + diff --git a/tests/Dapper.Tests/FakeDbTests.Issue1649.cs b/tests/Dapper.Tests/FakeDbTests.Issue1649.cs new file mode 100644 index 000000000..f9068ba28 --- /dev/null +++ b/tests/Dapper.Tests/FakeDbTests.Issue1649.cs @@ -0,0 +1,58 @@ +#if !NET481 +using System; +using System.Collections.Generic; +using System.Data; +using pengdows.crud.fakeDb; +using Xunit; + +namespace Dapper.Tests +{ + public sealed class FakeDbIssue1649Tests + { + [Fact] + public void TypeHandlerIsUsedForEachListExpansionParameter() + { + var handler = new EncodedValueHandler(); + SqlMapper.AddTypeHandler(handler); + try + { + using var conn = new fakeDbConnection(new FakeDataStore()); + conn.EnqueueNonQueryResult(2); + conn.Open(); + + var count = conn.Execute("select 1 where Id in @ids", new + { + ids = new[] { new EncodedValue(7), new EncodedValue(8) } + }); + + Assert.Equal(2, count); + Assert.Equal(new[] { "encoded:7", "encoded:8" }, handler.Values); + } + finally + { + SqlMapper.ResetTypeHandlers(); + } + } + + private readonly struct EncodedValue + { + public EncodedValue(int value) => Value = value; + public int Value { get; } + } + + private sealed class EncodedValueHandler : SqlMapper.TypeHandler + { + public List Values { get; } = new(); + + public override EncodedValue Parse(object value) => new(Convert.ToInt32(value)); + + public override void SetValue(IDbDataParameter parameter, EncodedValue value) + { + var encoded = $"encoded:{value.Value}"; + Values.Add(encoded); + parameter.Value = encoded; + } + } + } +} +#endif