存储过程中可为空的 Table 值参数

Nullable Table Valued Parameter in Stored Procedure

我有这个程序

CREATE PROCEDURE dbo.spProcedure1
    @intArray as dbo.intArray READONLY
AS
BEGIN
-- ...
END

使用用户类型作为参数

CREATE TYPE dbo.IntArray AS TABLE (IntValue int NULL)

我正在从 C# ASP.NET MVC 4 项目中调用该过程

    // creating empty SQL @IntArray parameter

        var emptyIntDataTable = new DataTable();
        emptyIntDataTable.Columns.Add("IntValue");

    // calling stored procedure

        return Database.SqlQuery<int>(
            @"spProcedure1 @p1",
            new SqlParameter("p1", (object)Utils.ToDataTable(m.IntArray) ?? emptyIntDataTable)
        ).ToList();

    // ToDataTable method which is returning null

        public static DataTable ToDataTable<T>(this IList<T> data)
                {
                    if (data == null)
                        return null;

                    ... // code omitted because it is not working yet                
}

调用存储过程抛出的错误是

The table type parameter 'p1' must have a valid type name.

如何传递空 table 值?

传递列表而不是数据table抛出以下错误

var emptyIntDataTable = new List<int>;

No mapping exists from object type System.Collections.Generic.List`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] to a known managed provider native type.

在您的代码中:

上面写着:

return Database.SqlQuery<int>(
    @"spProcedure1 @p1", new SqlParameter("p1", 
        (object)Utils.ToDataTable(m.IntArray) ?? emptyIntDataTable)
).ToList();

改为:

 return m.IntArray.Length > 0? 
        Database.SqlQuery<int>(@"spProcedure1 @p1",
            new SqlParameter("p1", 
               (object)Utils.ToDataTable(m.IntArray))).ToList():
        Database.SqlQuery<int>(@"spProcedure1")).ToList();

展示如何不传递 table 参数的示例

CREATE TYPE dbo.KeyIds]
AS TABLE(pkId int NOT NULL,
PRIMARY KEY CLUSTERED (pkId ASC)
WITH (IGNORE_DUP_KEY = OFF))
Go
-- ------------------------------
Create procedure testProc 
    @aIds dbo.keyIds readonly
as 
Set NoCount On
    if exists (select * from @aIds) 
        Select * from @aIds
    else
        Select 'No Aids passed in'
Go
-- ------------------------------

Exec dbo.testProc -- <--- Here I am NOT passing the @aids parameter

但是,即使我没有传递@aids 参数 它仍然有效,子查询 (select * from @aIds) 仍然有效,并且由于它是空数据 table SP returns 空消息 'No Aids passed in'.

另一方面,如果你传递参数

Declare @MyIds dbo.keyIds
Insert @MyIds Values(1)
Insert @MyIds Values(2)
Insert @MyIds Values(3)
Insert @MyIds Values(4)
Insert @MyIds Values(5)
Exec dbo.testProc @MyIds -- <--- Here I AM passing the @aids parameter

输出datatable参数的内容

C# 代码示例...

 public DataTable GetAccountTransactions(IEnumerable<int> accountIds)
    {
        const string procName = "FetchAccountTransactionData";

        var acctIds = accountIds == null ? 
              new List<int>() : accountIds.ToList();
        // -------------------------------------------------
        var parms = DbParamList.Make(); 
            // DbParamList is a List<IDbDataParameter>
        // See here, ONLY ADD PARAMETER if list is NOT empty!
        if (acctIds.Count > 0) 
            parms.AddSQLTableParm("aIds", acctIds);

        try
        { // following constructs command obkect and calls SP
            return Utilities.GetDataTable(schemaNm + "." + 
                            procName, parms, copConn);
        }
        catch (SqlException dbX)
        { 
           // Exception stuff
        }
    }


    public class DbParamSortedList : SortedList<string,IDbDataParameter> { }

备选方案

准备将List<int>转换成dbo.IntArray类型的方法

public static DataTable IntArrayToDataTable(IEnumerable<int> ids)
    {
        if (ids == null)
            return null;

        DataTable table = new DataTable();

        // datatable columns has to have same name as database type !
        table.Columns.Add("IntValue", typeof(int));
        foreach (int id in ids)
        {
            table.Rows.Add(id);
        }
        return table;
    }

运行 sql 存储过程

var sqlParameters = new List<object>();

    var parameter1 = Utils.IntArrayToDataTable(m.IntArray);    
    if (parameter1 != null) 

        sqlParameters.Add(new SqlParameter("intArray", parameter1)
        // these variables are the key, without them it is not working
        { 
            SqlDbType = SqlDbType.Structured,
            TypeName = "dbo.IntArray"
        });
else // parameter cannot be omitted !! even if all parameters are named !! otherwise parameter mismatch happens (in case of multiple parameters)
 sqlParameters.Add(new SqlParameter("intArray", SqlDbType.Structured) { TypeName = "dbo.IntArray" });


var sqlQuery = "spProcedure1 @InArray";

return Database.SqlQuery<int>(sqlQuery, sqlParameters.ToArray()).ToList();