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

Posted

技术标签:

【中文标题】访问存储的 Proc 输出参数时出现间歇性空引用错误【英文标题】:Intermittent Null Reference Errors When Accessing Stored Proc Output Parameter 【发布时间】: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

【问题讨论】:

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

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

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

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

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

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

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

【讨论】:

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

以上是关于访问存储的 Proc 输出参数时出现间歇性空引用错误的主要内容,如果未能解决你的问题,请参考以下文章

在 Azure 网站中使用 Azure SDK 时出现间歇性 SSL/TLS 错误

Safari 访问受 Windows 集成身份验证 (aka NTLM) 保护的网站时出现问题

使用字符串类型参数访问枚举时出现 TypeScript TS7015 错误

导航回另一帧时出现空对象引用错误

将折线与本机地图一起使用时出现空引用错误

使用 Kotlin 在片段中引用 RecyclerView 时出现空指针错误