SqlDataReader挂起在Dispose()上

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

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 } } 

在读取和处理数据时,可能会发生一些exception。 问题是抛出exception时DataReader挂起在Close()调用上。 你有什么想法为什么??? 以及如何以正确的方式解决这个问题? 当我在finally处理读者之前写了try..catch..finally阻塞而不是using和调用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(); } } } 

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

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

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

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

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

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

强调我加入了。 如果你再看看Close方法( 链接 ),它会说明:

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

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

我不会那样格式化。
打开(); 不在try块中,它可以抛出exception
的ExecuteReader(); 不在try块中,它可以抛出exception
我喜欢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(); }