Skip to content

Commit

Permalink
fix(TypeHandler): use the custom type handler when packing a list par…
Browse files Browse the repository at this point in the history
…ameter

Fixes DapperLib#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.
  • Loading branch information
Timothée Lecomte committed Apr 3, 2024
1 parent 402f20c commit da6a2ef
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 6 deletions.
22 changes: 16 additions & 6 deletions Dapper/SqlMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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();
Expand All @@ -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);
}
}
Expand Down
18 changes: 18 additions & 0 deletions tests/Dapper.Tests/TypeHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<RatingValue>
{
new() { Value = 1 },
new() { Value = 2 },
}
};

var result = connection.Query<int>("SELECT 1 WHERE 1 IN @ListOfRatingValues", parameters).Single();
Assert.Equal(1, result);
}

public class StringListTypeHandler : SqlMapper.TypeHandler<List<string>>
{
private StringListTypeHandler()
Expand Down

0 comments on commit da6a2ef

Please sign in to comment.