2011-08-08 41 views
29

Estoy intentando pasar un parámetro de valor de tabla a un procedimiento almacenado, pero sigo obteniendo una excepción (consulte a continuación).Cómo pasar un parámetro de valor de tabla

SqlCommand c = new SqlCommand("getPermittedUsers", myConn) { CommandType = CommandType.StoredProcedure }; 

c.Parameters.AddWithValue("@intNotifyingUserId", notifyingUserId); 
c.Parameters.AddWithValue("@tSelectedPdfIds", sharedPdfs).SqlDbType = SqlDbType.Structured; 

SqlDataReader dr = c.ExecuteReader();

El tipo está definido en el servidor de la siguiente manera:

CREATE TYPE [dbo].[IdList] AS TABLE(
    [Id] [int] NOT NULL 
)

he tratado de pasar sharedPdfs como List<int> y IQueryable<int>, pero obtengo la siguiente excepción:

Object must implement IConvertible.

¿Alguien sabe lo que estoy haciendo mal? La documentación implica que debería poder pasar una lista como TVP pero no dar ningún ejemplo.

Gracias.

+0

¿Cómo se declara 'getPermittedUsers'? –

+0

Este es un tiro largo, pero ¿ha intentado pasar un 'int []'? –

+4

Necesita usar 'DataTable'. – ChaosPandion

Respuesta

12

Puede pasar el parámetro como DataTable, IEnumerable<SqlDataRecord> o DbDataReader.

+0

? al SQLCommand ¿Estás seguro? –

+1

Sí, esos son los tipos admitidos para un parámetro de valor de tabla en SqlCommand. La documentación completa está aquí: http://msdn.microsoft.com/en-us/library/bb675163.aspx. Vea 1/3 del camino hacia abajo: "System.Data.SqlClient admite rellenar los parámetros con valores de tabla de DataTable, DbDataReader o System.Collections.Generic.IEnumerable ([T: System.Collections.Generic.IEnumerable' 1)] objetos ". –

+1

¿esto también es posible en Oracle? –

42

El siguiente ejemplo ilustra el uso de un bien o un DataTableIEnumerable<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# Código

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; 
    } 
} 
+1

Esto me ayudó, ¡gracias! – s0nica

+0

estaba intentando que tvp funcionara a partir de lo que encontré en http://sqlperformance.com/2012/08/t-sql-queries/splitting-strings-now-with-less-t-sql, pero seguí obteniendo los valores de dbnull. Su ejemplo terminó trabajando para mí, podría ser el parameter.typename. ¡Gracias! – Phil

+0

NB: para aquellos que no desean crear un tipo personalizado para esto, una solución alternativa es pasar datos como un tipo xml, luego consultar en contra de eso. – JohnLBevan

Cuestiones relacionadas