SqlDataReader hangs on Dispose()

本文关键字:Dispose on hangs SqlDataReader | 更新日期: 2023-09-27 18:37:25

我使用以下方法对数据库执行查询并读取数据:

using(SqlConnection connection = new SqlConnection("Connection string"))
{
    connection.Open();
    using(SqlCommand command = new SqlCommand("SELECT * FROM TableName", connection))
    {
        using (SqlDataReader reader = command.ExecuteReader())
        {
              // read and process data somehow (possible source of exceptions)
        } // <- reader hangs here if exception occurs
    } 
}

在读取和处理数据时,可能会发生一些异常。问题是当引发异常DataReader挂起Close()调用时。你有什么想法吗???以及如何以适当的方式解决这个问题?当我写try..catch..finally块而不是using并在finally中处置阅读器之前调用command.Cancel()时,问题就消失了。

工作版本:

    using(SqlConnection connection = new SqlConnection("Connection string"))
    {
        connection.Open();
        using(SqlCommand command = new SqlCommand("SELECT * FROM TableName", connection))
        {
            SqlDataReader reader = command.ExecuteReader();
            try
            {
                // read and process data somehow (possible source of exceptions)
            }
            catch(Exception ex)
            {
                // handle exception somehow
            }
            finally
            {
               command.Cancel(); // !!!
               reader.Dispose();
            }
        } 
    }

SqlDataReader hangs on Dispose()

发生异常时,在收到所有数据之前停止处理数据。即使在几行后中止处理,也可以重现此问题。

释放命令或读取器时,查询仍在服务器上运行。 ADO.NET 只是疯狂地读取所有剩余的行和结果集并将它们扔掉。它这样做是因为服务器正在发送它们,而协议需要接收它们。

调用SqlCommand.Cancel会向 SQL Server 发送"注意",从而导致查询真正中止。这与在SSMS中按取消按钮相同。

总而言之,只要您停止处理行,尽管入站的行更多,就会发生此问题。您的解决方法(调用SqlCommand.Cancel)是正确的解决方案。

关于SqlDataReaderDispose方法,MSDN(链接)是这样说的:

释放 DbDataReader 使用的资源并调用 Close

强调由我添加。如果你然后去看一下Close方法(链接),它指出:

Close 方法填充输出参数的值,返回 值和"记录受到影响",增加关闭所需的时间 用于处理大型或复杂查询的 SqlDataReader。 当返回值和受查询影响的记录数时 不重要,关闭 SqlDataReader 所需的时间 可以通过调用关联的 Cancel 方法来减少 调用 Close 方法之前的 SqlCommand 对象。

因此,如果您需要停止遍历阅读器,最好先取消命令,就像您的工作版本一样。

我不会那样格式化它。
打开();不在 try 块中,它可以引发异常
执行读取器();不在 try 块中,它可以引发异常
我喜欢读者。关闭 - 因为这是我在 MSDN 示例中
看到的我捕获SQL异常,因为它们有数字(例如超时)

SqlConnection connection = new SqlConnection();
SqlDataReader reader = null;
try
{
    connection.Open();  // you are missing this as a possible source of exceptions
    SqlCommand command = new SqlCommand("SELECT * FROM TableName", connection);
    reader = command.ExecuteReader();  // you are missing this as a possible source of exceptions
    // read and process data somehow (possible source of exceptions)
}
catch (SqlException ex)
{
}
catch (Exception ex)
{
    // handle exception somehow
}
finally
{
    if (reader != null) reader.Close();
    connection.Close();
}