SQL Server 如何从.net代码向存储过程传递表值参数

8tntrjer  于 2022-11-21  发布在  .NET
关注(0)|答案(6)|浏览(147)

我有一个SQL Server 2005数据库。在一些过程中,我将表参数作为nvarchar(用逗号分隔)传递给存储过程,并在内部划分为单个值。我将其添加到SQL命令参数列表中,如下所示:

cmd.Parameters.Add("@Logins", SqlDbType.NVarchar).Value = "jim18,jenny1975,cosmo";

我必须将数据库迁移到SQL Server 2008。我知道有表值参数,也知道如何在存储过程中使用它们。但我不知道如何在SQL命令中将表值参数传递给参数列表。
有人知道Parameters.Add过程的正确语法吗?或者有其他方法传递此参数吗?

vmpqdwk3

vmpqdwk31#

DataTableDbDataReaderIEnumerable<SqlDataRecord>对象可用来填入MSDN文章Table-Valued Parameters in SQL Server 2008 (ADO.NET)中所述的数据表值参数。
下列范例说明如何使用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;
    }
}
2cmtqfgy

2cmtqfgy2#

根据Ryan的回答,如果您要处理的table-valued parameter包含多个列,并且这些列的序数不是按字母顺序排列,则还需要设置DataColumnOrdinal属性。
例如,如果您有以下表值,该值用作SQL中的参数:

CREATE TYPE NodeFilter AS TABLE (
  ID int not null
  Code nvarchar(10) not null,
);

在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

如果未能执行此操作,则会出现解析错误,即无法将nvarchar转换为int。

fcg9iug3

fcg9iug33#

通用

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;

    }
kkih6yb8

kkih6yb84#

使用它的最简洁的方式。假设您的表是一个名为“dbo.tvp_Int”的整数列表(为您自己的表类型定制)
创建此扩展方法...

public static void AddWithValue_Tvp_Int(this SqlParameterCollection paramCollection, string parameterName, List<int> data)
{
   if(paramCollection != null)
   {
       var p = paramCollection.Add(parameterName, SqlDbType.Structured);
       p.TypeName = "dbo.tvp_Int";
       DataTable _dt = new DataTable() {Columns = {"Value"}};
       data.ForEach(value => _dt.Rows.Add(value));
       p.Value = _dt;
   }
}

现在,只需执行以下操作,即可在任意位置的一行中添加表值参数:

cmd.Parameters.AddWithValueFor_Tvp_Int("@IDValues", listOfIds);
7hiiyaii

7hiiyaii5#

使用以下代码从您的类型创建合适的参数:

private SqlParameter GenerateTypedParameter(string name, object typedParameter)
{
    DataTable dt = new DataTable();

    var properties = typedParameter.GetType().GetProperties().ToList();
    properties.ForEach(p =>
    {
        dt.Columns.Add(p.Name, Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType);
    });
    var row = dt.NewRow();
    properties.ForEach(p => { row[p.Name] = (p.GetValue(typedParameter) ?? DBNull.Value); });
    dt.Rows.Add(row);

    return new SqlParameter
    {
        Direction = ParameterDirection.Input,
        ParameterName = name,
        Value = dt,
        SqlDbType = SqlDbType.Structured
    };
}
f45qwnt8

f45qwnt86#

如果您有一个带参数的表值函数,例如以下类型:

CREATE FUNCTION [dbo].[MyFunc](@PRM1 int, @PRM2 int)
RETURNS TABLE
AS
RETURN 
(
    SELECT * FROM MyTable t
    where t.column1 = @PRM1 
    and t.column2 = @PRM2
)

你是这么说的:

select * from MyFunc(1,1).

然后,您可以从C#呼叫它,如下所示:

public async Task<ActionResult> MethodAsync(string connectionString, int? prm1, int? prm2)
{
  List<MyModel> lst = new List<MyModel>();

  using (SqlConnection connection = new SqlConnection(connectionString))
  {
     connection.OpenAsync();

     using (var command = connection.CreateCommand())
     {
        command.CommandText = $"select * from MyFunc({prm1},{prm2})";
        using (var reader = await command.ExecuteReaderAsync())
        {
           if (reader.HasRows)
           {
              while (await reader.ReadAsync())
              {
                 MyModel myModel = new MyModel();
                 myModel.Column1 = int.Parse(reader["column1"].ToString());
                 myModel.Column2 = int.Parse(reader["column2"].ToString());
                 lst.Add(myModel);
              }
            }
         }
     }
  }
  View(lst);
}

相关问题