如何判断SqlConnection是否有附加的SqlDataReader?

现在这更像是一种好奇而不是实际目的。 如果您打开了一个SqlConnection并将SqlDataReader附加到它,然后尝试使用相同的SqlConnection运行另一个查询,那么它将抛出一个错误。 我的问题是SqlConnection如何知道读者是否附加到它。 HasDataReader没有公共属性或任何东西,所以SqlConnection类如何知道?

原始问题:(不再相关)

嗨,我正在为连接池和我们发生的更常见的错误设置一个小东西(它总是一个简单的修复,但我们只是记不起reader.Close() !)它是我们什么时候许多类/方法使用的连接和一个方法打开数据读取器并忘记关闭它。 这并不是很糟糕,因为很多时候你需要做的就是进入调试器并升级到一级并在它之前看到它并检查它是否有一个未闭合的数据读取器。

现在,这是一个更大的问题。 在这个连接池中,如果一个datareader是打开的,那么直到一个线程获得连接并尝试使用它并且最初打开数据读取器的东西可能不再存在时,它就不可知。

非常简单,如何检测数据读取器是否在连接上打开,是否有办法在不关闭连接的情况下关闭阅读器?

SqlConnection如何知道读者是否附加到它上面

据我所见,SQLConnection知道它附有一个阅读器,因为它在内部维护了对它的引用。

明智地使用Reflector表明SQLConnection对象具有DBConnectionInternal类型的私有字段,该字段由此抽象类的许多具体实现之一填充。 当您尝试向连接添加第二个实时阅读器时,会在内部连接上调用方法“ValidateConnectionForExecute”,并且会跟踪内部“ReferenceCollection”的检查。 当这显示现有的实时阅读器时,会抛出exception。

我想,如果你愿意的话,你可以在运行时用reflection来挖掘所有这些。

确保关闭数据区(和数据库连接)的方法是始终在using块中打开它们,如下所示:

 using (SqlDataReader rdr = MySqlCommandObject.ExecuteReader()) { while (rdr.Read()) { //... } } // The SqlDataReader is guaranteed to be closed here, even if an exception was thrown. 

没人真的回答了伯爵的问题。 (“你为什么这样做?”不是答案。)我认为答案是你无法通过查看连接本身来判断连接是否有与之关联的开放数据阅读器。 该连接不会暴露任何会告诉您的属性。 打开连接将其State属性设置为ConnectionState.Open。 在其上打开数据读取器不会更改连接状态。 ConnectionState.Fetching等状态值仅在数据操作(如SqlDataReader.Read())正在进行时使用。 当连接刚好位于Reads之间时,连接状态就是Open。 因此,要确定打开的阅读器何时使用连接,您必须检查可能正在使用它的阅读器的状态。

哇..很多人没有回答这个问题! 没有人提到的是multithreading应用程序。 我想这里的每个人都有这样一个事实,你必须关闭读者,但我似乎没有看到任何人在寻址的事实是当下一个请求进来时读者可能无法完成。例如..我有一张桌子这是通过一个单独的线程填充,以便我保持UI交互。 在连接使用时让第二个第三个和第四个线程等待会很好。 然后,当它释放出来做生意。 如果没有一个简洁的方法来确定连接是否连接了一个阅读器,我必须花几分钟为MIGHT想要使用连接的每个类中的每个读者创建一些静态布尔标志系统。 比需要复杂得多

然后尝试使用相同的SqlConnection运行另一个查询然后它将抛出一个错误。

当然,您可以启用多个活动结果集 – 然后它不会抛出。 当然有一些限制(不是总有?),但它会起作用。 当然,这仅用于嵌套操作。 如果问题是你不小心打开了一些东西(你应该已经关闭),那么答案是(如前所述) using

为避免这种情况,请将DataReader包装在using块中,这样可以保证它像这样处理连接:

 using (IDataReader reader = command.ExecuteReader()) { //do stuff } 

IDataReader上有一个名为IsClosed的属性,它会告诉你它的状态。

检查它是否打开,如果是,请关闭它。 单挑,如果您使用的是SqlHelper类,这是一个错误 – 它在某些情况下不会关闭连接。 解决方案是在代码中使用try / catch或使用块,具体取决于您是否是2.0之前的版本。

您也可以使用委托,如果由于任何原因您无法使用using clausule,那么举例说明如何实现:

 public delegate void TransactionRunner(DbConnection sender, DbTransaction trans, object state); public void RunTransaction(TransactionRunner runner, object state) { RunTransaction(runner, IsolationLevel.ReadCommitted, state); } public void RunTransaction(TransactionRunner runner, IsolationLevel il, object state) { DbConnection cn = GetConnection from pool DbTransaction trans = null; try { trans = cn.BeginTransaction(il); runner(cn, trans, state); trans.Commit(); } catch (Exception err) { if (trans != null) trans.Rollback(); throw err; } finally { //Here you can close anything that was left open } } 

然后当你需要使用它时,只需使用该函数并将函数传递为

 public void DoStuff(){ TransactionRunner tr = new TransactionRunner(MyFunction); RunTransaction(tr, ); } public void DoStuffInternal(DbConnection cn, DbTransaction trans, object state){ //Do Stuff and Im sure that the transaction will commit or rollback } 

这似乎是.Net 3.5中的一个矫枉过正,但这就是我们当时在.Net 1.0中做到的…希望它有帮助……

今天我也遇到了同样的情况但是……网上没有运气。

因此,我编写了以下代码来查找是否在连接中打开了阅读器,或者通常查找是否已准备好使用连接:

 private bool IsConnectionReady(SqlConnection Connection) { bool nRet = true; try { String sql = "SELECT * FROM dummy_table"; using (SqlCommand cmd = new SqlCommand(sql, Connection)) { using (SqlDataReader rdr = cmd.ExecuteReader()) { } } } catch (Exception ex) { nRet = false; } return nRet; } 

“dummy_table”是我的数据库中的空虚表,用于检查可访问性。

这只是一种解决方法,但我应该让事情有效,并且能够在任何情况下检查连接可用性。

所以,我希望它对你有所帮助。

根据文章 ,即使您使用了使用块,也应该在完成后关闭阅读器。 使用块将关闭连接,但不会关闭阅读器。 为什么不一致? 甘拜下风。

使用新创建的命令对象创建具有相同连接和新数据读取器的新命令对象。 这样可以正常工作。