¿Cómo saber si una SqlConnection tiene un SqlDataReader adjunto?

Esto es algo ahora más curioso que real. Si tiene una SqlConnection abierta y adjunta un SqlDataReader a ella, y luego intenta ejecutar otra consulta utilizando el mismo SqlConnection , arrojará un error. Mi pregunta es cómo sabe SqlConnection que un lector está conectado a él. No hay una propiedad pública ni nada para HasDataReader , entonces, ¿cómo sabe la class SqlConnection ?

Pregunta original: (que ya no es relevante)

Hola, estoy preparando una pequeña cosa para la agrupación de conexiones y sobre los errores más comunes que tenemos (siempre es una solución fácil, pero no podemos recordar el reader.Close() ) es cuando tenemos una connection que es utilizada por muchas classs / methods y un método abre un lector de datos y olvida cerrarlo. Esto no es realmente malo porque muchas veces todo lo que tienes que hacer es entrar en el depurador y upload un nivel y ver la function antes y verificar si tiene un lector de datos no cerrado.

Ahora, aquí está el problema más grande. En este set de conexiones, si un lector de datos está abierto, no se conoce hasta que un hilo obtiene una connection e intenta usarlo y lo que originalmente abrió el lector de datos puede que ya no esté vivo.

Entonces, simplemente, ¿cómo se puede detectar si un lector de datos está abierto en una connection y hay alguna manera de cerrar el lector sin cerrar la connection?

¿Cómo sabe SqlConnection que un lector está conectado a él?

Hasta donde puedo ver, SQLConnection sabe que tiene un lector conectado porque mantiene una reference interna.

Un uso juicioso de Reflector muestra que el object SQLConnection tiene un campo privado de tipo DBConnectionInternal, que se completa con una de una serie de implementaciones concretas de esta class abstracta. Cuando intenta agregar un segundo lector en vivo a la connection, se llama al método 'ValidateConnectionForExecute' en la connection interna, y esto se traduce en un examen de una 'ReferenceCollection' interna. Cuando esto revela un lector en vivo existente, se lanza una exception.

Supongo que, si quisieras, podrías cavar todo esto tú mismo en el time de ejecución con reflexión.

La forma de asegurarse de cerrar los lectores de datos (y las conexiones a la database) es abrirlos siempre en un bloque de uso, de esta manera:

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

Nadie realmente respondió la pregunta de Earlz. ("¿Por qué lo haces de esa manera?" No es una respuesta.) Creo que la respuesta es que no se puede decir si una connection tiene un lector de datos abierto asociado simplemente mirando la connection. La connection no expone ninguna propiedad que te diga eso. La apertura de una connection establece su propiedad de estado en ConnectionState.Open. Abrir un lector de datos en él no cambia el estado de la connection. Los valores de estado como ConnectionState.Fetching se usan solo mientras las operaciones de datos como SqlDataReader.Read () están en progreso. Cuando la connection se encuentra entre Read, el estado de la connection es simplemente Open. Entonces, para determinar cuándo un lector abierto está utilizando la connection, debe verificar los estados de los lectores que podrían estar usándola.

wow … ¡Mucha gente que no responde la pregunta! Lo que nadie menciona es aplicaciones multiprocess. Creo que todos aquí tienen el hecho de que deben cerrar el lector, pero lo que no parece ver a nadie abordar es el hecho de que el lector puede no estar terminado cuando entra la siguiente request. Por ejemplo … Tengo una table eso se completa a través de un hilo separado para que preserve la interacción de UI. Sería bueno tener el segundo tercer y cuarto hilos esperando mientras la connection está en uso. Luego, cuando se libere, hazlo por negocios. Sin una forma clara de determinar si la connection tiene un lector conectado, tengo que pasar varios minutos creando algún tipo de sistema de bandera booleana estática para cada lector en cada class que PUEDA MANTENER la connection. Mucho más complejo de lo necesario

y luego intente ejecutar otra consulta utilizando el mismo SqlConnection y arrojará un error.

por supuesto, podría habilitar Conjuntos de resultados activos múltiples, y luego no lanzarlos. Hay algunas limitaciones por supuesto (¿no hay siempre?), Pero funcionará. Por supuesto, esto solo está destinado a operaciones de anidamiento . Si el problema es que accidentalmente has dejado algo abierto (que ya deberías haber cerrado), entonces la respuesta es (como ya se dijo) using .

Para evitar esto, ajuste su DataReader en un bloque de uso, esto garantizará que disponga la connection de la siguiente manera:

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

Hay una propiedad en IDataReader llamada IsClosed que le indicará su estado.

Compruebe si está abierto, y si es así, ciérrelo. Heads-up, si está utilizando la class SqlHelper, este es un error; no cierra la connection en algunos escenarios. La solución es usar try / catch o usar bloques en tu código, dependiendo de si eres pre-2.0 o no.

También puede usar delegates, si por alguna razón no puede usar el uso de clausule, aquí hay un ejemplo de cómo lograr eso:

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

Luego, cuando necesite usar esto, simplemente use la function y pase la function como

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

Esto parece una exageración ahora en .Net 3.5, pero así fue como lo hicimos en .Net 1.0 … Espero que ayude …

Hoy también me encontré en la misma situación pero … no tuve suerte en la web.

Entonces, escribí el siguiente código para encontrar si un lector se abre en una connection o, en general, si una connection está list para ser utilizada:

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

El "dummy_table" es una tabla ficticia vacía en mi db para verificar la accesibilidad.

Esto es solo una solución, pero debería hacer que las cosas funcionen y poder verificar la disponibilidad de la connection en cualquier caso.

Entonces, espero que te ayude.

Según el artículo , siempre debe cerrar el lector después de que haya terminado, incluso si usa un bloque de uso. Un bloque que usa cerrará una connection, pero no cerrará un lector. ¿Por qué la inconsistencia? Me gana

Acabo de tropezar con esta vieja pregunta. Encontré un artículo sobre cómo lograr esto a través de la reflexión:

http://blogs.msdn.com/b/dataaccesstechnologies/archive/2009/04/08/how-to-find-out-the-data-reader-referencing-an-ado-net-connection-object-to- fix-the-error-there-is-already-an-open-datareader-associated-with-this-command-which-must-be-closed-first.aspx