2011-08-08 65 views
29

我試圖將表值參數傳遞給存儲過程,但我不斷收到異常(請參見下文)。如何傳遞表值參數

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();

類型是在服務器上定義是這樣的:

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

我試圖通過sharedPdfs作爲List<int>,並IQueryable<int>,但不斷收到以下異常:

Object must implement IConvertible.

任何人都知道我在做什麼錯了?文件意味着我應該能夠通過一個列表作爲TVP,但沒有給出任何例子。

謝謝。

+0

'getPermittedUsers'如何申報? –

+0

這是一個長效的,但你有沒有嘗試傳遞一個'int []'? –

+4

你需要使用'DataTable'。 – ChaosPandion

回答

12

您可以將參數作爲DataTableIEnumerable<SqlDataRecord>DbDataReader傳遞。

+0

?到SQLCommand您確定嗎? –

+1

是的,這些是SqlCommand中Table-Value參數支持的類型。完整的文檔位於:http://msdn.microsoft.com/en-us/library/bb675163.aspx。請參見下面的1/3: 「System.Data.SqlClient支持從DataTable,DbDataReader或System.Collections.Generic.IEnumerable 填充表值參數([T:System.Collections.Generic.IEnumerable' 1)]對象「。 –

+1

這在Oracle中也是可行的嗎? –

42

下面的例子說明使用一個DataTableIEnumerable<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; 
    } 
} 
+1

這幫了我,謝謝! – s0nica

+0

試圖從我在http://sqlperformance.com/2012/08/t-sql-queries/splitting-strings-now-with-less-t-sql上找到的東西獲得tvp的工作,但不斷獲得dbnull值。你的例子最終爲我工作,可能是parameter.typename。謝謝! – Phil

+0

注意:對於那些不希望爲此創建自定義類型的用戶,解決方法是將數據作爲xml類型傳遞,然後根據該類型進行查詢。 – JohnLBevan