我希望这不是我之后拍的自己的问题之一,但这真让我感到困惑。我有这个为我的另一个存储过程工作,这就是为什么这是如此令人困惑。两者的设置基本相同。这就是发生的事情。
以下是我的存储过程示例:
ALTER PROCEDURE [dbo].[CreateRecord]
-- Add the parameters for the stored procedure here
@Link1Id INT = NULL,
@Link2Id INT = NULL,
@Amount MONEY,
@Output int out
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SET @Output = 0
-- Insert statements for procedure here
IF @Link1Id = NULL
BEGIN
IF NOT EXISTS(SELECT * FROM dbo.Records WHERE Link2Id = @Link2Id)
INSERT INTO [dbo].[Records]
([Link1Id]
,[Link2Id])
VALUES
(@Link1Id
,@Link2Id)
SET @Output = (SELECT RecordId FROM dbo.Records WHERE Link2Id = @Link2Id)
END
ELSE
BEGIN
IF NOT EXISTS(SELECT * FROM dbo.Records WHERE Link1Id = @Link1Id)
INSERT INTO [dbo].[Records]
([Link1Id]
,[Link2Id])
VALUES
(@Link1Id
,@Link2Id)
SET @Output = (SELECT RecordId FROM dbo.Records WHERE Link1Id = @Link1Id)
END
END
现在,我创建了一个基本上运行此过程的单元测试,并尝试Assert
返回的@Output
大于0,但@Output
参数永远不会有值在代码中的SqlCommand
上。这是一些C#代码:
private int ExecuteNonQueryWithOutput(string procedureName, SqlParameter[] parameters)
{
SqlCommand command = this.GenerateCommand(procedureName, parameters);
connection.Open();
command.ExecuteNonQuery();
int retval = (int)command.Parameters[OUTPUT].Value;
connection.Close();
return retval;
}
现在,我可以跳过调用ExecuteNonQuery()
的行,并在数据库中验证新的(和正确的)记录是否存在,但是在下一行,它在调用时抛出异常{ {1}}因为(int)command.Parameters[OUTPUT].Value;
不存在。
这对于我所拥有的另一个以完全相同的方式设置的程序非常有效。你知道它为什么不在这里工作吗?
谢谢,我有点难过。我已经调试了一段时间而没有运气。修改
生成参数数组的代码:
Value
答案 0 :(得分:2)
我没看到你宣布@Output的位置。你的意思是:
ALTER PROCEDURE [dbo].[CreateRecord]
-- Add the parameters for the stored procedure here
@Link1Id INT = NULL,
@Link2Id INT = NULL,
@Amount MONEY,
@Output INT = NULL OUTPUT
AS
此外,我并不是100%确定您具有检索命名输出参数的语法。但是参数必须存在才能引用它。如何在不声明@Output的情况下保存该存储过程?
答案 1 :(得分:1)
代码中有许多问题超出了输出参数问题。
要回答实际问题,您可能会将NULL值作为输出传回。当它尝试将其转换为Int时,您会收到错误。
也是sql行:
IF @Link1ID = null
总是会失败。在SQL术语中,null是一个不确定的值,因此(null!= null)。测试空值的方法是使用IS
。例如:
IF (@Link1ID is null)
这让我相信你实际上在sql代码中遇到了主键违规。
现在,进入更大的问题。您的C#代码存在缺陷。永远不会丢弃命令对象,如果有任何问题,您的连接对象也不会被丢弃。由于可用的sql连接耗尽,这将导致有趣的sql错误。
应该如下所示:
private int ExecuteNonQueryWithOutput(string procedureName, SqlParameter[] parameters)
{
int retval = 0;
using (SqlConnection conn = new SqlConnection("connection string here"))
using (SqlCommand command = this.GenerateCommand(procedureName, parameters)) {
connection.Open();
command.ExecuteNonQuery();
retval = (int)command.Parameters[OUTPUT].Value;
}
return retval;
}
请注意,这会在本地声明,使用和处理您的连接和命令对象。如果出现问题,这将确保资源得到妥善处理。
另请注意,它不使用全局“连接”对象。操作系统提供的连接池在根据需要打开/关闭连接时非常有效。因此,最佳做法是实例化并将它们保持足够长的时间来处理当前操作。它打开的时间越长,就越有可能遇到问题。