Dapper: .Net Core 3.1 DataTable DynamicParameter throws InvalidCastException

Created on 2 Apr 2020  Â·  5Comments  Â·  Source: StackExchange/Dapper

Hi

I have recently upgraded my .NetCore 2.2 WebApi to .NetCore 3.1, using Dapper 2.0.30.

Almost everything works as before the upgrade, except for trying to pass a TVP to my stored procs with DynamicParameters.

Take this example code:

public async Task<IEnumerable<int>> GetSomeData(IEnumerable<int> idFilter)
{
        using (var con = _connectionFactory.GetCoreConnection())
    {
            var dataTable = GetDataTable(idFilter);

            var dynamicParams = new DynamicParameters(new { IdFilter = dataTable });

            return await con.QueryAsync<int>("[dbo].[GetDataWithTVP]", dynamicParams, commandType: CommandType.StoredProcedure);
    }
}

private static DataTable GetDataTable(IEnumerable<int> idFilter)
{
    var dt = new DataTable();
    dt.Columns.Add("Id", typeof(int));

    if (idFilter == null) return dt;

    foreach (var id in idFilter)
    {
        dt.Rows.Add(id);
    }

    return dt;
}

Calling this example stored proc:

CREATE PROC [dbo].[GetDataWithTVP] (@IdFilter [dbo].[IdFilter] READONLY)
AS
BEGIN

    SET NOCOUNT ON;

    SELECT Id
      FROM @IdFilter;
END;

GO

At runtime, the code throws the following exception
System.InvalidCastException: 'Failed to convert parameter value from a DataTable to a IEnumerable'1.'

Any ideas as to why this is happening? I have loads of different proc calls with the same, and different TVPs, all throwing the same error.

Any help is greatly appreciated. Thanks!

All 5 comments

I will investigate.

On Thu, 2 Apr 2020, 09:00 Spencer Mayne, notifications@github.com wrote:

Hi

I have recently upgraded my .NetCore 2.2 WebApi to .NetCore 3.1, using
Dapper 2.0.30.

Almost everything works as before the upgrade, except for trying to pass a
TVP to my stored procs with DynamicParameters.

Take this example code:

public async Task> GetSomeData(IEnumerable idFilter)
{
using (var con = _connectionFactory.GetCoreConnection())
{
var dataTable = GetDataTable(idFilter);

      var dynamicParams = new DynamicParameters(new { IdFilter = dataTable });

      return await con.QueryAsync<int>("[dbo].[GetDataWithTVP]", dynamicParams, commandType: CommandType.StoredProcedure);

}
}

private static DataTable GetDataTable(IEnumerable idFilter)
{
var dt = new DataTable();
dt.Columns.Add("Id", typeof(int));

if (idFilter == null) return dt;

foreach (var id in idFilter)
{
dt.Rows.Add(id);
}

return dt;
}

Calling this example stored proc:

CREATE PROC [dbo].[GetDataWithTVP] (@IdFilter [dbo].[IdFilter] READONLY)
AS
BEGIN

SET NOCOUNT ON;

SELECT Id
  FROM @IdFilter;

END;

GO

At runtime, the code throws the following exception
System.InvalidCastException: 'Failed to convert parameter value from a
DataTable to a IEnumerable'1.'

Any ideas as to why this is happening? I have loads of different proc
calls with the same, and different TVPs, all throwing the same error.

Any help is greatly appreciated. Thanks!

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/StackExchange/Dapper/issues/1431, or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAAEHMARRKPWOFW5HYFHV7LRKRAZHANCNFSM4L2DGPTQ
.

Hi; I put together a test in the branch above ^^^ - I cannot repro - it is coming back green on all TFMs, on both System.Data.SqlClient and Microsoft.Data.SqlClient

Can you help me get a minimal repro?

image

As a side note: for conveying a list of integers, you may be interested in Dapper's custom in support:

... where Id in @ids ...

and in the C#:

``` c#
int[] ids = {...};
var users = connection.Query(@"
select * from Users
where Id in @ids", new { ids }).AsList();

Dapper can rewrite this in a range of ways, including (if you opt into it, because of server compatibility) using `string_split`; the following tells dapper to use multiple parameters for 0-4 values, and `string_split` for 5 or more:

``` c#
Dapper.SqlMapper.Settings.InListStringSplitCount = 5;

Additional question: is there any particular reason you're using DynamicParameters here? it doesn't seem necessary in this case, and I'm wondering if simply not doing that fixes it

@mgravell thanks so much for the quick turnaround on this! Having looked at your test, I noticed you're referencing System.Data.SqlClient, which I wasn't aware was required, I certainly wasn't referencing prior to the framework upgrade. Adding this solves this for me :D

I had no idea about the custom in functionality, I will definitely start using this to save some dev time, thanks for the suggestion!

K, in that case it sounds like this is simply a SqlClient version issue, i.e. the feature you're using requires a particular level of SqlClient; we're using the current version (4.8.1) in production quite happily, FWIW.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

silkfire picture silkfire  Â·  4Comments

nhathongly picture nhathongly  Â·  3Comments

yozawiratama picture yozawiratama  Â·  5Comments

julealgon picture julealgon  Â·  3Comments

Abdallah-Darwish picture Abdallah-Darwish  Â·  3Comments