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
)是正确的解决方案。
关于SqlDataReader
的Dispose
方法,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(); }