Cómo pasar parámetros de valores de tabla al procedimiento almacenado desde código .net
Tengo una base de datos SQL Server 2005. En algunos procedimientos tengo parámetros de tabla que paso a un proceso almacenado como nvarchar
(separados por comas) y los divido internamente en valores únicos. Lo agrego a la lista de parámetros del comando SQL así:
cmd.Parameters.Add("@Logins", SqlDbType.NVarchar).Value = "jim18,jenny1975,cosmo";
Tengo que migrar la base de datos a SQL Server 2008. Sé que existen parámetros de valores de tabla y sé cómo usarlos en procedimientos almacenados. Pero no sé cómo pasar uno a la lista de parámetros en un comando SQL.
¿Alguien conoce la sintaxis correcta del Parameters.Add
procedimiento? ¿O hay otra forma de pasar este parámetro?
DataTable
Se pueden utilizar objetos DbDataReader
, u objetos para completar un parámetro con valores de tabla según el artículo de MSDN Parámetros con valores de tabla en SQL Server 2008 (ADO.NET) .IEnumerable<SqlDataRecord>
El siguiente ejemplo ilustra el uso de a DataTable
o an IEnumerable<SqlDataRecord>
:
Código 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ódigo 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;
}
}
Además de la respuesta de Ryan, también deberá establecer la DataColumn
propiedad Ordinal
si se trata de un table-valued parameter
con varias columnas cuyos ordinales no están en orden alfabético.
Como ejemplo, si tiene el siguiente valor de tabla que se utiliza como parámetro en SQL:
CREATE TYPE NodeFilter AS TABLE (
ID int not null
Code nvarchar(10) not null,
);
Necesitaría ordenar sus columnas como tales en 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
Si no hace esto, obtendrá un error de análisis, no se pudo convertir nvarchar a int.
Genérico
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;
}