Dapper
Dapper copied to clipboard
Explicitly implemented ICustomQueryParameter fails with an ArgumentNullException
When a custom parameter explicitly implements the ICustomQueryParameter interface Dapper throws and ArgumentNullException.
See the following example (modified from an existing Unit Test)
private class IntExplicitCustomParam : SqlMapper.ICustomQueryParameter
{
private readonly IEnumerable<int> numbers;
public IntExplicitCustomParam(IEnumerable<int> numbers)
{
this.numbers = numbers;
}
void SqlMapper.ICustomQueryParameter.AddParameter(IDbCommand command, string name)
{
command.CommandType = CommandType.StoredProcedure;
var number_list = CreateSqlDataRecordList(command, numbers);
// Add the table parameter.
AddStructured(command, number_list);
}
}
[Fact]
public void TestExplicitTVPWithAnonymousObject()
{
try
{
connection.Execute("CREATE TYPE int_list_type AS TABLE (n int NOT NULL PRIMARY KEY)");
connection.Execute("CREATE PROC get_ints @integers int_list_type READONLY AS select * from @integers");
var nums = connection.Query<int>("get_ints", new { integers = new IntExplicitCustomParam(new int[] { 1, 2, 3 }) }, commandType: CommandType.StoredProcedure).ToList();
Assert.Equal(1, nums[0]);
Assert.Equal(2, nums[1]);
Assert.Equal(3, nums[2]);
Assert.Equal(3, nums.Count);
}
finally
{
try
{
connection.Execute("DROP PROC get_ints");
}
finally
{
connection.Execute("DROP TYPE int_list_type");
}
}
}
The issue is caused by the CreateParamInfoGenerator method using the PropertyType to find the method instead of typeof(ICustomQueryParameter), this can be seen in the following lines of code on the il.EmitCall line as GetMethod is unable to find that method:
if (typeof(ICustomQueryParameter).IsAssignableFrom(prop.PropertyType))
{
il.Emit(OpCodes.Ldloc, typedParameterLocal); // stack is now [parameters] [typed-param]
il.Emit(callOpCode, prop.GetGetMethod()); // stack is [parameters] [custom]
il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [custom] [command]
il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [custom] [command] [name]
il.EmitCall(OpCodes.Callvirt, prop.PropertyType.GetMethod(nameof(ICustomQueryParameter.AddParameter)), null); // stack is now [parameters]
continue;
}
I have created a PR to address this issue: https://github.com/DapperLib/Dapper/pull/1794