From da6a2efb4ef9ff5b21bebb461293d72c1d4f6e60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Lecomte?= Date: Wed, 3 Apr 2024 17:18:14 +0200 Subject: [PATCH] fix(TypeHandler): use the custom type handler when packing a list parameter Fixes #2067 The code for `SqlMapper.PackListParameters` was not using the custom type handler. This caused an error like `No mapping exists from object type xxxxx to a known managed provider native type.` when passing a collection parameter to a query, when the type of the collection items is not natively supported by the SQL client, and when a custom `TypeHandler` is registered for it. Here this is fixed by using the same logic that is already in `DynamicParameters.AddParameter`. A unit test is added to cover the scenario. --- Dapper/SqlMapper.cs | 22 ++++++++++++++++------ tests/Dapper.Tests/TypeHandlerTests.cs | 18 ++++++++++++++++++ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/Dapper/SqlMapper.cs b/Dapper/SqlMapper.cs index 5575a6f5f..961af2f28 100644 --- a/Dapper/SqlMapper.cs +++ b/Dapper/SqlMapper.cs @@ -2168,6 +2168,7 @@ public static void PackListParameters(IDbCommand command, string namePrefix, obj if (list is not null && !viaSplit) { object? lastValue = null; + SqlMapper.ITypeHandler? handler = null; foreach (var item in list) { if (++count == 1) // first item: fetch some type info @@ -2178,7 +2179,7 @@ public static void PackListParameters(IDbCommand command, string namePrefix, obj } if (!isDbString) { - dbType = LookupDbType(item.GetType(), "", true, out var handler); + dbType = LookupDbType(item.GetType(), "", true, out handler); } } var nextName = namePrefix + count.ToString(); @@ -2199,14 +2200,23 @@ public static void PackListParameters(IDbCommand command, string namePrefix, obj } } - var tmp = listParam.Value = SanitizeParameterValue(item); - if (tmp is not null && tmp is not DBNull) - lastValue = tmp; // only interested in non-trivial values for padding + if (handler is null) + { + var tmp = listParam.Value = SanitizeParameterValue(item); + if (tmp is not null && tmp is not DBNull) + lastValue = tmp; // only interested in non-trivial values for padding - if (DynamicParameters.ShouldSetDbType(dbType) && listParam.DbType != dbType.GetValueOrDefault()) + if (DynamicParameters.ShouldSetDbType(dbType) && listParam.DbType != dbType.GetValueOrDefault()) + { + listParam.DbType = dbType.GetValueOrDefault(); + } + } + else { - listParam.DbType = dbType.GetValueOrDefault(); + if (DynamicParameters.ShouldSetDbType(dbType)) listParam.DbType = dbType.GetValueOrDefault(); + handler.SetValue(listParam, item ?? DBNull.Value); } + command.Parameters.Add(listParam); } } diff --git a/tests/Dapper.Tests/TypeHandlerTests.cs b/tests/Dapper.Tests/TypeHandlerTests.cs index 80c3292ff..28d6d8d2f 100644 --- a/tests/Dapper.Tests/TypeHandlerTests.cs +++ b/tests/Dapper.Tests/TypeHandlerTests.cs @@ -425,6 +425,24 @@ public void SO24740733_TestCustomValueSingleColumn() Assert.Equal(200, foo.Value); } + [Fact] + public void Issue2067_TestCustomValueCollection() + { + SqlMapper.AddTypeHandler(RatingValueHandler.Default); + + var parameters = new + { + ListOfRatingValues = new List + { + new() { Value = 1 }, + new() { Value = 2 }, + } + }; + + var result = connection.Query("SELECT 1 WHERE 1 IN @ListOfRatingValues", parameters).Single(); + Assert.Equal(1, result); + } + public class StringListTypeHandler : SqlMapper.TypeHandler> { private StringListTypeHandler()