SqlDataReader挂起Dispose()

时间:2013-10-30 15:07:26

标签: c# .net sqldatareader

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

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块而不是command.Cancel()并调用了finally时,问题就出现了。

工作版:

    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();
            }
        } 
    }

3 个答案:

答案 0 :(得分:6)

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

当处理命令或阅读器时,查询仍在服务器上运行。 ADO.NET只是像疯了一样读取所有剩余的行和结果集并抛弃它们。这样做是因为服务器正在发送它们,协议需要接收它们。

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

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

答案 1 :(得分:2)

关于Dispose的{​​{1}}方法,MSDN(link)可以这样说:

  

释放DbDataReader使用的资源,调用关闭

我强调的重点。如果您继续查看SqlDataReader方法(link),则会说明:

  

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

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

答案 2 :(得分:-1)

我不会那样格式化。
打开();不在try块中,它可以抛出异常
的ExecuteReader();不在try块中,它可以抛出异常
我喜欢reader.Close - 因为这是我在MSDN样本中看到的 我捕获SQLexception,因为它们有数字(比如超时)

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();
}