如何从.net代码传递表值参数到存储过程
我有一个MS SQL Server 2005数据库。 在几个过程中,我有表参数,我把它传递给一个存储过程作为一个nvarchar(用逗号分隔)和内部分为单个值。 我将它添加到SQL命令参数列表中,如下所示:
cmd.Parameters.Add("@Logins", SqlDbType.NVarchar).Value = "jim18,jenny1975,cosmo";
我必须将数据库迁移到SQL Server 2008.我知道有表值参数,我知道如何在存储过程中使用它们。 但我不知道如何传递一个SQL命令中的参数列表。 有没有人知道正确的Parameters.Add
程序语法? 还是有另一种方式来传递这个参数?
DataTable
, DbDataReader
或IEnumerable<SqlDataRecord>
对象可用于填充SQL Server 2008(ADO.NET)中 MSDN文章表值参数的表值参数 。
以下示例说明如何使用DataTable
或IEnumerable<SqlDataRecord>
:
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#代码 :
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; } }
除了Ryan的回答之外,如果你正在处理一个具有多列的table-valued parameter
,其序号不是按字母顺序的,你还需要设置DataColumn
的Ordinal
属性。
作为一个例子,如果你有下面的表值被用作SQL中的一个参数:
CREATE TYPE NodeFilter AS TABLE ( ID int not null Code nvarchar(10) not null, );
你需要在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
如果你不这样做,你会得到一个parsing错误,无法将nvarchar转换为int。
通用
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; }
最简洁的方法来处理它。 假设你的表是一个名为“dbo.tvp_Int”的整数列表(为你自己的表types定制)
创build这个扩展方法…
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; } }
现在你可以在任何地方添加一个表值参数,只要这样做:
cmd.Parameters.AddWithValueFor_Tvp_Int("@IDValues", listOfIds);