[英]How to pass table value parameters to stored procedure from .net code
I have a SQL Server 2005 database.我有一个 SQL Server 2005 数据库。 In a few procedures I have table parameters that I pass to a stored proc as an
nvarchar
(separated by commas) and internally divide into single values.在一些过程中,我将表参数作为
nvarchar
(以逗号分隔)传递给存储过程,并在内部分为单个值。 I add it to the SQL command parameters list like this:我将它添加到 SQL 命令参数列表中,如下所示:
cmd.Parameters.Add("@Logins", SqlDbType.NVarchar).Value = "jim18,jenny1975,cosmo";
I have to migrate the database to SQL Server 2008. I know that there are table value parameters, and I know how to use them in stored procedures.我必须将数据库迁移到SQL Server 2008。我知道有表值参数,我知道如何在存储过程中使用它们。 But I don't know how to pass one to the parameters list in an SQL command.
但我不知道如何将一个传递给 SQL 命令中的参数列表。
Does anyone know correct syntax of the Parameters.Add
procedure?有谁知道
Parameters.Add
过程的正确语法? Or is there another way to pass this parameter?还是有另一种方法来传递这个参数?
DataTable
, DbDataReader
, or IEnumerable<SqlDataRecord>
objects can be used to populate a table-valued parameter per the MSDN article Table-Valued Parameters in SQL Server 2008 (ADO.NET) . DataTable
、 DbDataReader
或IEnumerable<SqlDataRecord>
对象可用于根据 MSDN 文章SQL Server 2008 (ADO.NET) 中的表值参数填充表值参数。
The following example illustrates using either a DataTable
or an IEnumerable<SqlDataRecord>
:以下示例说明了使用
DataTable
或IEnumerable<SqlDataRecord>
:
SQL Code : SQL 代码:
CREATE TABLE dbo.PageView
(
PageViewID BIGINT NOT NULL CONSTRAINT pkPageView PRIMARY KEY CLUSTERED,
PageViewCount BIGINT NOT NULL
);
CREATE TYPE dbo.PageViewTableType AS TABLE
(
PageViewID BIGINT NOT NULL
);
CREATE PROCEDURE dbo.procMergePageView
@Display dbo.PageViewTableType READONLY
AS
BEGIN
MERGE INTO dbo.PageView AS T
USING @Display AS S
ON T.PageViewID = S.PageViewID
WHEN MATCHED THEN UPDATE SET T.PageViewCount = T.PageViewCount + 1
WHEN NOT MATCHED THEN INSERT VALUES(S.PageViewID, 1);
END
C# Code : C#代码:
private static void ExecuteProcedure(bool useDataTable,
string connectionString,
IEnumerable<long> ids)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = "dbo.procMergePageView";
command.CommandType = CommandType.StoredProcedure;
SqlParameter parameter;
if (useDataTable) {
parameter = command.Parameters
.AddWithValue("@Display", CreateDataTable(ids));
}
else
{
parameter = command.Parameters
.AddWithValue("@Display", CreateSqlDataRecords(ids));
}
parameter.SqlDbType = SqlDbType.Structured;
parameter.TypeName = "dbo.PageViewTableType";
command.ExecuteNonQuery();
}
}
}
private static DataTable CreateDataTable(IEnumerable<long> ids)
{
DataTable table = new DataTable();
table.Columns.Add("ID", typeof(long));
foreach (long id in ids)
{
table.Rows.Add(id);
}
return table;
}
private static IEnumerable<SqlDataRecord> CreateSqlDataRecords(IEnumerable<long> ids)
{
SqlMetaData[] metaData = new SqlMetaData[1];
metaData[0] = new SqlMetaData("ID", SqlDbType.BigInt);
SqlDataRecord record = new SqlDataRecord(metaData);
foreach (long id in ids)
{
record.SetInt64(0, id);
yield return record;
}
}
Further to Ryan's answer you will also need to set the DataColumn
's Ordinal
property if you are dealing with a table-valued parameter
with multiple columns whose ordinals are not in alphabetical order.除了 Ryan 的回答之外,如果您正在处理具有多个列的序数不是按字母顺序排列的
table-valued parameter
,您还需要设置DataColumn
的Ordinal
属性。
As an example, if you have the following table value that is used as a parameter in SQL:例如,如果您将下表值用作 SQL 中的参数:
CREATE TYPE NodeFilter AS TABLE (
ID int not null
Code nvarchar(10) not null,
);
You would need to order your columns as such in C#:您需要在 C# 中对列进行排序:
table.Columns["ID"].SetOrdinal(0);
// this also bumps Code to ordinal of 1
// if you have more than 2 cols then you would need to set more ordinals
If you fail to do this you will get a parse error, failed to convert nvarchar to int.如果你不这样做,你会得到一个解析错误,无法将 nvarchar 转换为 int。
Generic通用的
public static DataTable ToTableValuedParameter<T, TProperty>(this IEnumerable<T> list, Func<T, TProperty> selector)
{
var tbl = new DataTable();
tbl.Columns.Add("Id", typeof(T));
foreach (var item in list)
{
tbl.Rows.Add(selector.Invoke(item));
}
return tbl;
}
The cleanest way to work with it.使用它的最干净的方式。 Assuming your table is a list of integers called "dbo.tvp_Int" (Customize for your own table type)
假设您的表是一个名为“dbo.tvp_Int”的整数列表(为您自己的表类型定制)
Create this extension method...创建此扩展方法...
public static void AddWithValue_Tvp_Int(this SqlParameterCollection paramCollection, string parameterName, List<int> data)
{
if(paramCollection != null)
{
var p = paramCollection.Add(parameterName, SqlDbType.Structured);
p.TypeName = "dbo.tvp_Int";
DataTable _dt = new DataTable() {Columns = {"Value"}};
data.ForEach(value => _dt.Rows.Add(value));
p.Value = _dt;
}
}
Now you can add a table valued parameter in one line anywhere simply by doing this:现在,您只需执行以下操作即可在任意位置的一行中添加表值参数:
cmd.Parameters.AddWithValueFor_Tvp_Int("@IDValues", listOfIds);
Use this code to create suitable parameter from your type:使用此代码从您的类型创建合适的参数:
private SqlParameter GenerateTypedParameter(string name, object typedParameter)
{
DataTable dt = new DataTable();
var properties = typedParameter.GetType().GetProperties().ToList();
properties.ForEach(p =>
{
dt.Columns.Add(p.Name, Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType);
});
var row = dt.NewRow();
properties.ForEach(p => { row[p.Name] = (p.GetValue(typedParameter) ?? DBNull.Value); });
dt.Rows.Add(row);
return new SqlParameter
{
Direction = ParameterDirection.Input,
ParameterName = name,
Value = dt,
SqlDbType = SqlDbType.Structured
};
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.