【问题标题】:SQL Server User-Defined Table Type and .NETSQL Server 用户定义的表类型和 .NET
【发布时间】:2021-08-17 03:10:53
【问题描述】:

我需要将一个整数数组从 .NET 传递给一个存储过程,因此我在 google 上搜索了这个主题并最终遇到了 Arrays and Lists in SQL Server 2008,它由 Erland Sommarskog 编写,并且被认为是执行此过程的标准方式.

我尝试了两种不同的方式来将用户定义的表类型传递给存储过程,但每一种方式都出现异常。这两种方式都类似于 Erland Sommarskog 在上面的链接中使用的方式。

方式一——使用DataTable作为SqlParameter

DataTable dt = new DataTable();
dt.Columns.Add("n", typeof(int));

// Just adding 3 test rows to the DataTable
DataRow dr = dt.NewRow();
dr["n"] = 1;
dt.Rows.Add(dr);

dr = dt.NewRow();
dr["n"] = 2;
dt.Rows.Add(dr);

dr = dt.NewRow();
dr["n"] = 3;
dt.Rows.Add(dr);

// Creation of the SqlParameter
SqlParameter p = new SqlParameter();
p.ParameterName = "@ids";
p.Direction = ParameterDirection.Input;
p.SqlDbType = SqlDbType.Structured;
p.TypeName = "lstInt_TblType";
p.Value = dt;

// Blows up here
DataSet ds = DAWrapper.GetDataSet(
    Common.GetDB(),
    "usp_Test",
    new SqlParameter[] { p });

我得到的例外状态: 传入的表格数据流 (TDS) 远程过程调用 (RPC) 协议流不正确。参数 1 ("@ids"):数据类型 0x62 (sql_variant) 的类型特定元数据的类型无效。

方式二——使用列表作为SqlParameter

List<SqlDataRecord> lstSDR = new List<SqlDataRecord>();
SqlMetaData[] tvp_definition = { new SqlMetaData("n", SqlDbType.Int) };

// Just adding 3 test rows
SqlDataRecord rec = new SqlDataRecord(tvp_definition);
rec.SetInt32(0, 50);
lstSDR.Add(rec);

rec = new SqlDataRecord(tvp_definition);
rec.SetInt32(0, 51);
lstSDR.Add(rec);

rec = new SqlDataRecord(tvp_definition);
rec.SetInt32(0, 52);
lstSDR.Add(rec);

// Creation of the SqlParameter
SqlParameter p = new SqlParameter();
p.ParameterName = "@ids";
p.Direction = ParameterDirection.Input;
p.SqlDbType = SqlDbType.Structured;
p.TypeName = "lstInt_TblType";
p.Value = lstSDR;

// Blows up here
DataSet ds = DAWrapper.GetDataSet(
    Common.GetDB(),
    "usp_Test",
    new SqlParameter[] { p });

我得到的例外是: 不存在从对象类型 System.Collections.Generic.List`1[[Microsoft.SqlServer.Server.SqlDataRecord, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] 到已知的映射托管提供程序本机类型。

其他信息 lstInt_TblType 是我的 SQL Server 2008 中的用户定义的表类型。它确实存在(我三重检查了这个!)。它有一列称为“n”,类型为 int,主键,不允许空值。我完全复制了 Erland 的设置方式。

我还验证了存储过程 usp_Test 可以在 SQL Server Manager Studio 中运行,因此我相当肯定异常不是从那个方向发出的。这是我用来验证存储过程是否有效的 t-sql:

DECLARE @ids lstInt_TblType
INSERT @ids(n) VALUES(1),(2),(3)
EXEC usp_Test ids

任何关于去哪里的建议将不胜感激。谢谢!

*编辑:* 存储过程usp_Test:

ALTER PROCEDURE [dbo].[usp_Test]
(
    @ids lstInt_TblType READONLY
)
AS
BEGIN

    SET NOCOUNT ON;

        select *
    from dbo.dat_MetaData
    where MetaDataTypeID in (select n from @ids)
END
GO

【问题讨论】:

  • 表类型是否定义在您要连接的同一个数据库中?
  • @Oded - 感谢您的提问。我只是仔细检查了它是否访问了正确的数据库,确实如此。
  • 还值得检查架构名称,可能会将架构名称附加到类型(即dbo.tvp_name)。
  • 我没有提到(虽然我不相信这会导致问题,但可能是错误的)我的数据访问层(DAWrapper)驻留在一个针对 3.5 框架的类库中,但实际的网络应用程序本身是针对 4.0 框架的。
  • @Oded - 刚刚尝试了“dbo.lstInt_TblType”,但对于这两种情况都不起作用。错误消息(对于 DataTable 尝试)让我认为它能够很好地找到用户定义的表类型,但由于某种原因它无法协调 DataRow 的“n”列的“int”类型。嗯......我想我会从数据库中用户定义的表类型中删除主键属性,看看这是否能让我到任何地方。

标签: asp.net sql-server-2008


【解决方案1】:

找到了一种不同的方式来做这件事。这种方式使用 System.Data.SqlClient 库来创建与数据库的连接,指定存储过程名称,然后将参数作为 DataTable 传入,该 DataTable 用作 SQL Server 用户定义的表类型。

using (SqlConnection conn = new SqlConnection(connStr)) {
    SqlCommand cmd = conn.CreateCommand();
    cmd.CommandType = System.Data.CommandType.StoredProcedure;
    cmd.CommandText = "dbo.usp_Test";
    cmd.Parameters.AddWithValue("@ids", dt);
    conn.Open();

    using (SqlDataReader sqlReader = cmd.ExecuteReader()) {
        DataTable retTbl = new DataTable();
        retTbl.Load(sqlReader);
    }
}

【讨论】:

    【解决方案2】:

    您还可以在 Microsoft 自己的 Table-Valued Parameters 参考中找到如何将表值参数数据传递到存储过程的好示例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-05-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-18
      • 1970-01-01
      • 1970-01-01
      • 2021-11-04
      相关资源
      最近更新 更多