DapperLib / DapperLib/Dapper

Explicitly implemented ICustomQueryParameter fails with an ArgumentNullException

Open
#1,793 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C#
Stars
18.4k
Forks
3.7k
Avg merge
5h 8m
Merged PRs (30d)
1

Description

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;
}

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in CreateParamInfoGenerator at the ICustomQueryParameter branch and inspect the il.EmitCall line shown in the issue. Use the existing TestExplicitTVPWithAnonymousObject unit test as the reproduction, then verify the stored-procedure query returns 1, 2, and 3 without throwing an ArgumentNullException.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, sql
Domain
database
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.