6

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

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块而不是在usingcommand.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();
            }
        } 
    }
4

3 回答 3

8

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

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

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

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

于 2013-10-30T17:10:16.863 回答
2

关于 的Dispose方法SqlDataReader,MSDN(链接)有这样的说法:

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

重点是我加的。然后,如果您查看Close方法(链接),它会说明:

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

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

于 2013-10-30T17:17:06.610 回答
-2

我不会那样格式化它。
打开(); 不在 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();
}
于 2013-10-30T15:58:44.160 回答