【问题标题】:Intermittent Null Reference Errors When Accessing Stored Proc Output Parameter访问存储的 Proc 输出参数时出现间歇性空引用错误
【发布时间】:2016-06-13 10:13:48
【问题描述】:

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

问题是我有时在尝试访问输出参数时得到“对象引用未设置为对象的实例”。我知道如果尝试访问输出参数时未关闭数据读取器,通常会收到此错误。但是,由于 datareader 是在 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

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

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

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

【问题讨论】:

  • 是的,我正在其他地方访问该值。
  • 你能展示你的存储过程吗?如果您遇到死锁,我想我已经看到了这样的错误。
  • 不幸的是,我无法显示确切的存储过程。但是,存储过程不做任何更新(只是选择),它使用快照隔离级别。我将尝试模拟死锁,看看这是否模拟了问题。谢谢。

标签: stored-procedures sql-server-2012 ado.net


【解决方案1】:

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

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

【讨论】:

  • 请看上面的评论。正在处理 Null。
【解决方案2】:

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

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

【讨论】:

  • 我试过类似的代码。问题是错误间歇性地发生。如果代码有问题,错误总会出现。
  • 你能从 SQL 中检查 firstParam 输出参数值,无论你在 C# 中遇到 NULL 引用错误的地方,我认为如果出现错误,你一定是从数据库中获取了一些奇怪的数据。
【解决方案3】:

使用'as'字符串关键字

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

【讨论】:

    【解决方案4】:

    我能够通过在数据库上运行 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
    

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

    【讨论】:

    • 实际的问题是在关闭datareader之前可能没有读取输出参数。 “Close 方法填充输出参数的值” - docs.microsoft.com/en-us/dotnet/api/…。所以僵局是一个红鲱鱼。
    【解决方案5】:

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

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

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

      顺便说一句。在你的能力中创建和使用commandBahavior() 是不必要的 - 你所拥有的是你的代码与reader = command.ExecuteReader() 完全相同

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

    4. 您是否为存储过程的某些运行返回多个记录集?我相信在这种情况下参数会被打乱,尽管我从未亲身体验过,以了解某些情况

    【讨论】:

    • 1.我已经检查过了。我有代码来处理 DBNull 的返回值。我还模拟了返回空值。 2.commandBehaviour 设置为closeConnection。但这不会影响输出参数的读取。 3. 是的,输出参数已正确初始化。 4. 我检查了多个记录集的返回。这不是问题。如前所述,问题是间歇性的,因此有一些外部因素会影响结果。感谢您的帮助。
    • 这种断断续续的性质确实令人费解。它表明参数正在丢失给垃圾收集器,但我并不完全相信。您是否有来自错误的堆栈跟踪。我最初想知道在哪个对象上检测到 null:cmd、参数或参数 [""]。如果原因在 sql.data 类中的某个地方(连接或数据传输等),我希望在 .value 访问器之前很久就会看到一个异常。不过,您是否可以在 sql (sql profiler) 上进行跟踪以捕获 sql 异常或警告。
    • 在参数 [""] 上检测到空值。我通过将 datareader 实例从 using 块中取出来模拟了确切的错误(和堆栈跟踪)(实际上在访问输出参数之前没有关闭阅读器)。我倾向于同意您的建议,即错误出在连接或数据传输的某个地方 - 问题在于模拟它。
    • 关闭阅读器之前的模拟失败向我表明您没有到达结果集的末尾。据我所知,参数仅在结果集的 all 完成后处理,即 sql 整个数据流的尾随部分。在我看来——无论对错——关闭阅读器只会将参数“释放”给客户端。话虽如此,我本以为会在某个时候发生异常(很可能是超时)。正如我之前指出的,它几乎就像 sql 发送的结果集超过 1 个,而您只读取第一个。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-16
    相关资源
    最近更新 更多