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

时间:2016-08-31 12:41:45

标签: sql-server-2008 asp.net-mvc-4 stored-procedures table-valued-parameters

我有这个程序

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                
}

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

  

表类型参数“p1”必须具有有效的类型名称。

如何传递空表值?

传递列表而不是数据表抛出跟随错误

var emptyIntDataTable = new List<int>;
  

对象类型System.Collections.Generic.List`1 [[System.Int32,mscorlib,Version = 4.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089]]不存在到已知托管提供程序本机类型的映射。

2 个答案:

答案 0 :(得分:0)

在您的代码中:

它说:

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

示例显示如何不传递表参数

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)仍然起作用,并且因为它是一个空数据表,所以SP返回空消息'No Aids pass 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

它输出数据表参数的内容

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> { }

答案 1 :(得分:0)

替代解决方案

准备将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();