我有一个存储过程,它接受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;
答案 0 :(得分:2)
您不需要AddWithValue
,也不需要在调用存储过程时指定UDTT名称(仅对参数化的即席SQL指定TypeName
)。 / p>
prms
集合中对象的类型是什么?作为SqlParameter.Value
(http://msdn.microsoft.com/en-us/library/bb675163.aspx):
所以,要做的主要是:
p.SqlDbType = SqlDbType.Structured;
cmd.Parameters.AddWithValue(p.ParameterName, p.Value.ToString());
设置值,摆脱整个if (prms.Keys.Contains(pname)) p.Value = prms[pname];
行。p.SqlDbType = SqlDbType.Structured;
移至switch (sx[0].ToLower())
并通过if (sx[0] == "table type")
摆脱else
阻止,但显然请保留cmd.Parameters.Add(p);
。