Dapper:帮助我使用多个用户定义的表类型运行存储过程

时间:2014-05-11 05:57:10

标签: c# sql stored-procedures dapper user-defined-types

我有一个包含3个输入参数的存储过程。

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

其中IntList是用户定义的表类型。

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

我的目标是用dapper调用这个存储过程。

我找到了一些关于使用dapper传递用户定义类型的示例。 其中一个是在Dapper.Microsoft.Sql nuget包中实现的TableValuedParameter类。

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();

上面写的代码抛出

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

我已经使用一个用户定义的表类型测试了我的存储过程,它运行正常。

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

我需要帮助运行原始存储过程。 谢谢。

3 个答案:

答案 0 :(得分:10)

Dapper有扩展方法来处理表值参数。

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

您可以通过以下方式使用dapper:

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

它将被翻译成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

答案 1 :(得分:0)

我在Call stored procedure from dapper which accept list of user defined table type处回复了类似的帖子,其中包含以下内容,我认为这也可能对您有所帮助!

我知道这有点旧了,但我想我会发布这个,因为我试图让这更容易一点。我希望我已经创建了一个我创建的NuGet包,它允许代码如下:

public class IntList
{
     public int Item { get; set; }
}

var list1 = new List<IntList>{new IntList{ Item= 1 }, new IntList{ Item= 2}};
var list2 = new List<IntList>{new IntList{ Item= 3 }, new IntList{ Item= 4}};

var parameters = new DynamicParameters();
parameters.Add("@Keyword", "stringValue");
parameters.AddTable("@CategoryIds", "IntList", list1);
parameters.AddTable("@MarketIds", "IntList", list2);

 var result = con.Query<int>("someSP", parameters, commandType: CommandType.StoredProcedure);

NuGet包:https://www.nuget.org/packages/Dapper.ParameterExtensions/0.2.0 仍处于早期阶段,因此可能无法解决所有问题!

请阅读自述文件并随时在GitHub上发表文章:https://github.com/RasicN/Dapper-Parameters

答案 2 :(得分:0)

这有点死板,但我相信有更好的方法可以做到这一点。具体来说,一种允许插入简单和复杂类型并执行单个更新的方法,如果您有很多索引,这可能很重要。

在此示例中,我将使用名为ProductAdd的存储过程和名为ProductList的用户定义表类型添加具有名称,ImageUrl和Year的产品。您可以简化为数字或字符串列表。

DataTable productList = new DataTable();
productList.Columns.Add(new DataColumn("Name", typeof(string)));
productList.Columns.Add(new DataColumn("ImageUrl", typeof(string)));
productList.Columns.Add(new DataColumn("Year", typeof(Int32)));
foreach (var product in products)
{
    // The order of these must match the order of columns for the Type in SQL!
    productList.Rows.Add(product.Name, product.ImageUrl, product.Year);
}

using (var connection = new SqlConnection(_appSettings.ConnectionString))
{
    await connection.OpenAsync();
    var result = new List<Product>();
    try
    {
        result = await connection.QueryAsync<Product>("[dbo].[ProductAdd]",
            new { product = productList },
            commandType: CommandType.StoredProcedure
        );
    }
    catch (SqlException ex)
    {
        // Permissions and other SQL issues are easy to miss, so this helps
        throw ex;
    }
}

return result;

用户定义类型的定义如下:

CREATE TYPE [dbo].[ProductList] AS TABLE(
    [Name] [nvarchar](100) NOT NULL,
    [ImageUrl] [nvarchar](125) NULL,
    [Year] [int] NOT NULL,
)

并且存储过程使用传入的列表作为表。如果您对GET / SELECT使用相同的逻辑并尝试使用IN,则将使用相同的逻辑。 SQL INJOIN的底线,因此我们在表上使用JOIN代替IN

CREATE PROCEDURE ProductAdd
    @product ProductList READONLY
AS
BEGIN
    SET NOCOUNT ON;

    INSERT INTO [Product] ([Name], [ImageUrl], [Year])          
    SELECT paramProduct.[Name]
        , paramProduct.[ImageUrl]
        , paramProduct.[Year]
    FROM @product AS paramProduct
        LEFT JOIN [Product] AS existingProduct
            ON existingProduct.[Name] = paramProduct.[Name] 
                AND existingProduct.[Year] = paramProduct.[Year]
    WHERE existingProduct.[Id] IS NULL
END
GO