如何传递表值参数

nw.*_*nw. 39 .net c# ado.net

我试图将表值参数传递给存储过程,但我不断得到一个异常(见下文).

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();
Run Code Online (Sandbox Code Playgroud)

类型在服务器上定义如下:

CREATE TYPE [dbo].[IdList] AS TABLE(
    [Id] [int] NOT NULL
)
Run Code Online (Sandbox Code Playgroud)

我试图通过sharedPdfs为List<int>,和IQueryable<int>,但不断收到以下异常:

Object must implement IConvertible.

谁知道我做错了什么?文档暗示我应该能够将列表作为TVP传递但不提供任何示例.

谢谢.

Rya*_*hel 49

以下示例说明使用a 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
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这在 .Net CORE 3.1 中不起作用。始终给出类型为“无法将参数值从 &lt;CreateSqlDataRecords&gt; 转换为 IEnumerable`1”的错误。 (2认同)

Jar*_*ore 16

你也可以传递参数作为DataTable,IEnumerable<SqlDataRecord>DbDataReader.


Chr*_*lum 5

添加带有更新链接的新答案。

根据文档(learn.microsoft.com),您可以使用以下参数类型之一:

SqlClient 支持从DataTableDbDataReader或 IEnumerable \ SqlDataRecord对象填充表值参数。必须使用SqlParameter 的TypeName属性指定表值参数的类型名称。TypeName 必须与先前在服务器上创建的兼容类型的名称匹配。

未包含在文档中,但对于高性能应用程序很重要,使用IEnumerable<SqlDataRecord>(.NET Core 3.1,异步)的示例:

  using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(30));
  using SqlConnection connection = this.GetConnection();
  await connection.OpenAsync(timeout.Token);

  using SqlCommand command = connection.CreateCommand();
  command.CommandType = CommandType.StoredProcedure;
  command.CommandText = "Mycommand";

  IEnumerable<SqlDataRecord> records = // ...

  SqlParameter parameter = command.Parameters.Add("@MyObjects", SqlDbType.Structured);
  parameter.TypeName = "MyCustomTableType";
  parameter.Value = records;

  await command.ExecuteNonQueryAsync(timeout.Token);
Run Code Online (Sandbox Code Playgroud)

使用DataTable 的示例:

  // Create a DataTable with the modified rows.  
  DataTable addedCategories = CategoriesDataTable.GetChanges(DataRowState.Added);  

  // Configure the SqlCommand and SqlParameter.  
  SqlCommand insertCommand = new SqlCommand("usp_InsertCategories", connection);  
  insertCommand.CommandType = CommandType.StoredProcedure;  
  SqlParameter tvpParam = insertCommand.Parameters.AddWithValue("@tvpNewCategories", addedCategories);  
  tvpParam.SqlDbType = SqlDbType.Structured;  

  // Execute the command.  
  insertCommand.ExecuteNonQuery(); 
Run Code Online (Sandbox Code Playgroud)

使用DbDataReader 的示例:

 // Assumes connection is an open SqlConnection.  
 // Retrieve data from Oracle.  
 OracleCommand selectCommand = new OracleCommand(  
     "Select CategoryID, CategoryName FROM Categories;",  
     oracleConnection);  
 OracleDataReader oracleReader = selectCommand.ExecuteReader(  
     CommandBehavior.CloseConnection);  

  // Configure the SqlCommand and table-valued parameter.  
  SqlCommand insertCommand = new SqlCommand(  
      "usp_InsertCategories", connection);  
  insertCommand.CommandType = CommandType.StoredProcedure;  
  SqlParameter tvpParam =  
      insertCommand.Parameters.AddWithValue(  
      "@tvpNewCategories", oracleReader);  
  tvpParam.SqlDbType = SqlDbType.Structured;  

  // Execute the command.  
  insertCommand.ExecuteNonQuery(); 
Run Code Online (Sandbox Code Playgroud)