Dapper: Help me run stored procedure with multiple user defined table types

Mike picture Mike · May 11, 2014 · Viewed 8.8k times · Source

I have a stored procedure with 3 input paramaters.

... PROCEDURE [dbo].[gama_SearchLibraryDocuments]
@Keyword nvarchar(160), 
@CategoryIds [dbo].[IntList] READONLY, 
@MarketIds [dbo].[IntList] READONLY ...

Where IntList is user defined table type.

CREATE TYPE [dbo].[IntList]
AS TABLE ([Item] int NULL);

My goal is to call this stored procedure with dapper.

I have found some examples regarding passing user defined type with dapper. One of them is TableValuedParameter class implemented in Dapper.Microsoft.Sql nuget package.

var list = conn.Query<int>("someSP", new
{
Keyword = (string)null,
CategoryIds = new TableValuedParameter<int>("@CategoryIds", "IntList", new List<int> { }),
MarketIds = new TableValuedParameter<int>("@MarketIds", "IntList", new List<int> { 541 })
}, commandType: CommandType.StoredProcedure).ToList();

Written above code throws

An exception of type 'System.NotSupportedException' occurred in Dapper.dll but was not handled in user code

Additional information: The member CategoryIds of type Dapper.Microsoft.Sql.TableValuedParameter`1[System.Int32] cannot be used as a parameter value

I have tested my stored procedure with one user defined table type and it worked fine.

conn.Query<int>("someSP", new TableValuedParameter<int>("@MarketIds", "IntList", new List<int> { 541 }), commandType: CommandType.StoredProcedure).ToList();

I need help with running original stored procedure. Thank you.

Answer

Dmitry picture Dmitry · Sep 11, 2014

Dapper has extension method to work with table valued parameters.

public static SqlMapper.ICustomQueryParameter AsTableValuedParameter(this DataTable table, string typeName = null)

You can use dapper in the following way:

var providersTable = new DataTable();
providersTable.Columns.Add("value", typeof(Int32));
foreach (var value in filterModel.Providers)
{
    providersTable.Rows.Add(value);
}
var providers = providersTable.AsTableValuedParameter("[dbo].[tblv_int_value]");

var filters =
    new
    {
        campaignId = filterModel.CampaignId,
        search = filterModel.Search,
        providers = providers,
        pageSize = requestContext.PageSize,
        skip = requestContext.Skip
    };

using (var query = currentConnection.QueryMultiple(StoredProcedureTest, filters, nhTransaction, commandType: CommandType.StoredProcedure))
{
    var countRows = query.Read<int>().FirstOrDefault();
    var temp = query.Read<CategoryModel>().ToList();
    return new Result<IEnumerable<CategoryModel>>(temp, countRows);
}

It will be translated into SQL:

declare @p3 dbo.tblv_int_value
insert into @p3 values(5)
insert into @p3 values(34)
insert into @p3 values(73)
insert into @p3 values(14)

exec [dbo].[StoredProcedureTest] @campaignId=123969,@search=NULL,@providers=@p3,@pageSize=20,@skip=0