我有一个包含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();
我需要帮助运行原始存储过程。 谢谢。
答案 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 IN
是JOIN
的底线,因此我们在表上使用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