输出变量未在SQL Server存储过程中设置

时间:2012-03-06 19:37:54

标签: c# .net sql-server-2008 tsql unit-testing

我希望这不是我之后拍的自己的问题之一,但这真让我感到困惑。我有这个为我的另一个存储过程工作,这就是为什么这是如此令人困惑。两者的设置基本相同。这就是发生的事情。

以下是我的存储过程示例:

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

2 个答案:

答案 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;
}

请注意,这会在本地声明,使用和处理您的连接和命令对象。如果出现问题,这将确保资源得到妥善处理。

另请注意,它不使用全局“连接”对象。操作系统提供的连接池在根据需要打开/关闭连接时非常有效。因此,最佳做法是实例化并将它们保持足够长的时间来处理当前操作。它打开的时间越长,就越有可能遇到问题。