访问存储过程输出参数时出现间歇性空引用错误

Intermittent Null Reference Errors When Accessing Stored Proc Output Parameter

我有一些访问存储过程的代码,使用数据读取器,returns 一些输出参数和结果集。数据读取器是使用 using 块创建的。

问题是我 有时 在尝试访问输出参数时得到 "Object Reference not set to an instance of an object"。我知道如果在尝试访问输出参数时未关闭数据读取器,通常会收到此错误。但是,因为数据读取器是在 using 块中访问的,并且访问输出参数的代码在 using 块之外,所以参数永远不能为 null。

这怎么可能?

SqlCommand command = new SqlCommand();
command.CommandText = "stored proc";

CommandBehavior commandBehavior = new CommandBehavior();

using (var reader = command.ExecuteReader(commandBehavior))
{
    while(reader.Read())
    {
        //access record values
    }
}

var param1 = command.Parameters["firstParam"].Value;  //<-- null reference error here

为简洁起见,我省略了一些代码。

VM 上的代码和 SQL 服务器 运行。

其他相关事实:C# .Net Framework 4.0,SQL Server 2012

您的代码结构看起来是正确的,这使我考虑了您省略的代码或存储过程中可能存在的可能原因。如果您已经检查过这些内容,我深表歉意。

  1. SQL 存储过程是否为某些查询的参数值返回空值?

  2. 在省略的代码中,是否有任何代码改变了 commandBehavior - 如果行为设置为 CloseConnection,那么这就是你的原因。

    顺便说一句。在您的 could 中创建和使用 commandBahavior() 是不必要的 - 您所拥有的是您的代码与 reader = command.ExecuteReader()

    完全相同
  3. 您是否在 command.ExecuteReader() 之前初始化了 command.Parameters["firstParam"] 并设置了 direction=output 并且类型正确。

  4. 您是否为存储过程的某些运行返回多个记录集?我相信在这种情况下参数会被打乱,尽管我从来没有亲身经历过,无法确定

请尝试以下代码,我已经测试过它工作正常。

SqlConnection con = new SqlConnection("Data Source=local;Initial Catalog=AdventureWorks;Integrated Security=SSPI;");
            SqlCommand command = new SqlCommand("Test",con);
            if (con.State == ConnectionState.Closed)
            {
                con.Open();
            }
            command.Parameters.AddWithValue("@parm1", "Hi");
            command.Parameters.Add("@parm2",SqlDbType.VarChar,500).Value = "";
            command.Parameters["@parm2"].Direction = ParameterDirection.Output;
            command.CommandType = CommandType.StoredProcedure;
            //CommandBehavior commandBehavior = new CommandBehavior();
            var parm2 = "";
            using (SqlDataReader reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    //access record values
                }
                reader.Close();
                parm2 = Convert.ToString(command.Parameters["@parm2"].Value);
            }

SP :

CREATE PROCEDURE Test
@parm1 varchar(100),
@parm2 Varchar(200) = '' OUTPUT 
AS
BEGIN
    SELECT 1 AS parm1
    SELECT @parm2 = 'Shakti Singh'
END

可能你的代码是正确的,你只需要在你提到的行上处理 null,请尝试用以下代码替换该行。

var param1 = Convert.ToString(command.Parameters["firstParam"].Value);

使用'as'字符串关键字

command.Parameters["@parm2"].Value as string

我能够通过数据库上的 运行 SQL 分析器跟踪问题。我很幸运地发现了一个错误。检查 SQL 日志显示存在死锁。但是,应用程序未收到错误。为了模拟错误,请尝试以下存储过程(从@Shakti 复制):

CREATE PROCEDURE Test
@parm1 varchar(100),
@parm2 Varchar(200) = '' OUTPUT 
AS
BEGIN
    SELECT 1 AS parm1
    SELECT * FROM some_table
    SELECT @parm2 = 'Shakti Singh' 
    THROW 1,'Deadlock Error!',1   --error number not important, any error here will simulate the problem, in my case a deadlock
END

在应用程序中,您将能够毫无错误地读取数据 reader,但您将无法访问输出参数。但是,如果您在 SELECT * 之前抛出错误,应用程序将收到错误,但您显然无法读取数据 reader.