用户定义的表类型(UDTT)参数是否可以与其他类型混合使用

时间:2014-10-31 20:44:44

标签: c# sql-server-2008 .net-4.5 table-valued-parameters

我有一个存储过程,它接受3个参数,第一个和第二个参数是varchar类型,最后一个是用户定义的表类型。

当我通过ExecuteNonQuery执行存储过程时,它会引发异常:

  

[System.Data.SqlClient.SqlException] = {"操作数类型冲突:nvarchar与ttOrderItems不兼容"}

ttOrderItems是用户定义的表类型。

这种行为是正常的吗?当其中一个参数是用户定义的表类型?

时,它不需要混合参数

以下是调用存储过程的代码片段:

public DataSet execProc(string storedProcedureName, IDictionary<string, object> prms = null)
        {
            using (SqlCommand cmd = new SqlCommand(storedProcedureName, scon))
            {
                DataSet rs = new DataSet();
                if (prms != null) SetupParams(storedProcedureName, cmd, prms);
                try
                {
                    cmd.CommandText = storedProcedureName;
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.Connection.Open();
                    //using (SqlDataAdapter da = new SqlDataAdapter(cmd)) da.Fill(rs);
                    //{
                    //    da.Fill(rs);
                    //}
                    cmd.ExecuteNonQuery();
                    cmd.Connection.Close();
                        return rs;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                finally
                {
                    scon.Close();
                }
            }
        }

private void SetupParams(string RoutineName, SqlCommand cmd, IDictionary<string, object> prms, bool keepConnectionOpen = true)
        {
            if (cmd != null) cmd.Parameters.Clear();
            string pname = "";
            DataTable tblParams = Select("Select * from dbo.ftRoutineSchema('" + RoutineName + "')");
            foreach (DataRow dr in tblParams.Rows)
            {
                System.Data.SqlClient.SqlParameter p = new System.Data.SqlClient.SqlParameter();
                pname = dr["COLUMnNAME"].ToString().ToLower();
                p.ParameterName = pname;
                pname = pname.Remove(0, 1).ToLower(); // remove @  sign
                if (prms.Keys.Contains(pname)) p.Value = prms[pname];
                string direction = dr["Direction"].ToString().ToLower();
                string sptype = (string)dr["DataType"];
                string[] sx = dr["DataType"].ToString().Split(new char[] { '(', ',', ')' });
                try
                {
                    #region case type switch
                    switch (sx[0].ToLower())
                    {
                        case "int":
                            p.DbType = DbType.Int32;//=int.Parse(sx[2]);
                            break;
                        case "bigint":
                            p.DbType = DbType.Int64;
                            break;
                        case "varchar":
                            p.DbType = DbType.String;
                            p.Size = int.Parse(sx[1]);
                            break;
                        case "nvarchar":
                            p.DbType = DbType.String;
                            p.Size = int.Parse(sx[1]);
                            break;
                        case "decimal":
                            p.DbType = DbType.Decimal;
                            break;
                        case "datetime":
                            p.DbType = DbType.DateTime;
                            break;
                        case "ntext":
                        case "text":
                            p.DbType = DbType.String;
                            p.Size = 65536;
                            break;

                        default:
                            break;
                    }
                    switch (direction)
                    {
                        case "in": p.Direction = ParameterDirection.Input; break;
                        case "out": p.Direction = ParameterDirection.Output; break;
                        case "rc": p.Direction = ParameterDirection.ReturnValue; break;
                        default: break;
                    }
                    #endregion
                    if (sx[0] == "table type")
                    {
                        p.SqlDbType = SqlDbType.Structured;

                        cmd.Parameters.AddWithValue(p.ParameterName, p.Value.ToString());
                    }
                    else
                        cmd.Parameters.Add(p);
                }

                catch (Exception ex)
                {
                    throw ex;
                }
            }
        }

当我通过以下T-SQL执行相同的proc时,它按预期工作:

use edi
go
declare @items dbo.ttOrderItems
insert @items
            select 1,'574114-023',1,'EA',720,'2014-Oct-14',null,null
union all select 2,'574116-035',8,'EA',1865.5,'2014-Oct-10',null,null
exec dbo.prCatalogItems '010','000164',@items

CREATE function [dbo].[ftCatalogItems](@comno varchar(3),@cuno varchar(6),@items  ttOrderItems readonly) returns table as
    /*-------------------------------------------------------
    DECLARE @COMNO VARCHAR(3)='010',@CUNO VARCHAR(6)='000164'
    declare @items ttOrderItems;
    insert @items( position,ItemCode        ,QtyOrdered ,UOM    ,PriceQuoted,RequiredBy     ,ExpectedOnDock ,BackOrdered)
    select                              1,'1231-221'    ,1                  ,'EA'   ,20.20          ,'2014-11-01'   ,'2014-11-01'       ,0
    union  select                   2,'110223-245',10                   ,'EA'   ,2001.20        ,'2014-11-01'   ,'2014-11-01'       ,0


    select * from @items
    --------------------------------------------------------*/
    return(

    select 
                Position
                ,ItemCode
                ,QtyOrdered
                ,'EA' UOM
            ,PriceQuoted
            ,RequiredBy
            ,Isnull(c.Net,0.00) Net
            ,[Qty.] QtyApplicable
                ,Status=case 
                    when ItemCode is null then 'Not in Catalog' 
                    when [From] > getdate() then 'Availle only on or after '+Convert(varchar(30),[From],106)
                    when  datediff(DD,getdate(),isnull(nullif([To],''),'4712-01-01')) < 1 then 'EXPIRED'
                    when items.PriceQuoted != c.Net then 'Quoted Price does not match Catalog price'
                    else coalesce(c.[Item Code],'Invalid/non-existent Item')
                end 

from @items items
Left Join ediCatalog c on ltrim(c.[Item Code])=[ItemCode] AND COMNO=@COMNO AND c.[Customer Id.] =@CUNO  and c.[Server]=dbo.fsBaanServer()

)

ALTER proc [dbo].[prCatalogItems](@comno varchar(3),@cuno varchar(6),
                                  @items ttOrderItems readonly) as
Begin
    select * from dbo.ftCatalogItems(@comno,@cuno,@items)
end;

1 个答案:

答案 0 :(得分:2)

您不需要AddWithValue,也不需要在调用存储过程时指定UDTT名称(仅对参数化的即席SQL指定TypeName)。 / p>

prms集合中对象的类型是什么?作为SqlParameter.Valuehttp://msdn.microsoft.com/en-us/library/bb675163.aspx):

,您有三种选择
  1. 数据表
  2. DbDataReader
  3. 返回IEnumerable的方法
  4. 所以,要做的主要是:

    1. 确保您通过上述其中一项
    2. 保留p.SqlDbType = SqlDbType.Structured;
    3. 不要使用`.ToString()&#39;;
    4. 通过cmd.Parameters.AddWithValue(p.ParameterName, p.Value.ToString());设置值,摆脱整个if (prms.Keys.Contains(pname)) p.Value = prms[pname];行。
    5. p.SqlDbType = SqlDbType.Structured;移至switch (sx[0].ToLower())并通过if (sx[0] == "table type")摆脱else阻止,但显然请保留cmd.Parameters.Add(p);