2009-11-12 11 views
11

Esto es algo más curiosidad que un propósito real. Si tiene un SqlConnection abierto y adjunta un SqlDataReader, y luego intenta ejecutar otra consulta utilizando el mismo SqlConnection, arrojará un error. Mi pregunta es cómo sabe el SqlConnection que un lector está conectado a él. No hay una propiedad pública ni nada por el , entonces, ¿cómo sabe la clase SqlConnection?¿Cómo saber si una SqlConnection tiene un SqlDataReader adjunto?


pregunta original: (que ya no es relevante)

Hola, Soy la creación de una pequeña cosa para la agrupación de conexiones y el de los errores más comunes que tenemos producirse (siempre es una solución fácil, pero no podemos recordar reader.Close()!) es cuando tenemos una conexión que es utilizada por muchas clases/métodos y un método abre un lector de datos y se olvida de cerrarlo. Esto no es realmente malo porque muchas veces todo lo que tienes que hacer es entrar en el depurador y subir un nivel y ver la función antes y verificar si tiene un lector de datos no cerrado.

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

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

+0

Solo para comprobar: no está intentando escribir su propio mecanismo de agrupación de conexiones ¿verdad? La agrupación de conexiones ya se maneja bajo las cubiertas del marco; no necesita hacer nada al respecto usted mismo. –

+2

Guau .. De hecho, no sabía que proporcionaba la agrupación de conexiones ... Supongo que es tan implícito y tan poco que tienes que hacer para habilitarlo que me lo perdí ... esta pregunta ya no es relevante. – Earlz

+0

@earlz - ¡todo es mejor! Vea esto para detalles: http://msdn.microsoft.com/en-us/library/8xx3tyca.aspx. –

Respuesta

13

¿cómo el SqlConnection saber que un lector está unido a

Por lo que yo puedo ver, el SQLConnection sabe que tiene un lector conectado a él, ya que mantiene una referencia a él internamente.

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

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

+0

+1, lo más probable es que el OP no obtenga una respuesta más precisa y precisa que esta. – stakx

+0

+1 buena respuesta - todo el respeto por @Joel y su consejo, pero esto en realidad * responde a la pregunta *. – slugster

+0

FWIW, la ruta completa al lector de datos es 'Connection. [Private] _innerConnection. [Private] ReferenceCollection. [Private] _items [i] .Target', donde' [Private] 'indica campos/propiedades no públicos y' i' es un índice – Arithmomaniac

13

La manera de asegurarse de que cierras los DataReaders (y conexiones de bases de datos) es abrir siempre en un bloque usando, así:

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

Esta es la forma en que lo hacemos también. Es importante tener en cuenta que esto solo funcionará para .NET 2.0 y superior. Si está utilizando una versión anterior, querrá usar un bloque Try/Finally y asegurarse de que se llama al método .Close() en el bloque Finally y asegúrese de buscar una referencia nula antes de llamar a Close(). –

+3

Eso está bien y bien, pero no responde mi pregunta. Hemos comenzado a hacer bloques 'using', pero todavía tenemos código antiguo que no lo hace. Al mantener ese código, es posible que suceda algo para que el lector no se cierre. ¿Cómo podemos detectar eso y lanzar un error? – Earlz

+2

Entonces, ¿no quiere cambiar su viejo para hacerlo bien, y en su lugar agregar un montón de código nuevo para detectar el mal? Eso es un poco al revés. –

0

Para evitar esto, envuelva su DataReader en un bloque usando, esto garantizará que se dispone la conexión de este modo:

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

Hay una propiedad en IDataReader llama IsClosed que le dirá su estado.

+0

Necesito saber si un lector de datos está abierto solo desde la conexión. No tendremos el lector de datos a nuestro alcance del código que debería verificar esto. – Earlz

+0

Eche un vistazo a la enumeración ConnectionState en SqlConnection, no estoy seguro, pero ConnectionState.Fetching o ConnectionState.Executing pueden darle lo que desea. Sin embargo, si no tiene una referencia al DataReader, será difícil limpiarlo sin cerrar la conexión y volver a abrir (al menos que yo sepa). Además, ¿está utilizando una referencia de conexión en toda la aplicación? –

0

Compruebe si está abierto, y si es así, ciérrelo. Heads-up, si está utilizando la clase SqlHelper, este es un error; no cierra la conexión 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.

0

Puede utilizar delegados también, si por alguna razón no se puede utilizar la cláusula de usar, aquí está un ejemplo de cómo llevar a cabo lo siguiente:

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 se necesita utilizar este sólo tiene que utilizar la función y pasar la función 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 era así como lo hicimos en ese entonces en .Net 1.0 ... Espero que ayuda ...

4

en realidad, nadie un 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 conexión tiene un lector de datos abierto asociado simplemente mirando la conexión. La conexión no expone ninguna propiedad que te diga eso. La apertura de una conexión establece su propiedad de estado en ConnectionState.Open. Abrir un lector de datos en él no cambia el estado de la conexión. Valores de estado como ConnectionState.La obtención solo se usa mientras las operaciones de datos, como SqlDataReader.Read() están en progreso. Cuando la conexión se encuentra entre Read, el estado de la conexión es simplemente Open. Entonces, para determinar cuándo un lector abierto está utilizando la conexión, debe verificar los estados de los lectores que podrían estar usándola.

+0

¿cómo puede un lector de datos marcar una conexión como "lector de datos abierto sin embargo"? No hay métodos o campos accesibles públicamente, entonces, ¿cómo le dice un lector de datos a una conexión que arroje un error si otra persona intenta usarlo? Eso es lo que no entiendo es en qué parte del código SqlConnection/SqlCommand detecta un lector de datos abierto – Earlz

+2

Aunque Joel no "responde" la pregunta, le da la forma correcta de hacer las cosas. Especialmente por el hecho de la complejidad de resolverlo de la manera incorrecta versus la solución correcta, yo desaprobaría (leer, escribir una advertencia en un archivo) a cualquier empleado que intentó una solución tan hackish en lugar de solo un bloque de USO. – Godeke

+1

Tenía la misma pregunta, esta es la respuesta exacta que estaba buscando. Tengo un DAL genérico reutilizable donde un objeto de conexión instanciado externamente pasa como parámetro y necesitaba determinar si era reutilizable o no. –

0

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

Así, escribí el siguiente código para encontrar si un lector se abre en una conexión o generalmente encontrar si una conexión está listo para ser utilizado:

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 conexión en cualquier caso.

Por lo tanto, espero que te ayude.

0

De acuerdo con article, siempre debe cerrar el lector después de que haya terminado, incluso si utiliza un bloque de uso. Un bloque que usa cerrará una conexión, pero no cerrará un lector. ¿Por qué la inconsistencia? Me gana

1

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

por supuesto, se podría permitir Multiple Active Result Sets - entonces no lo hace tiro. Hay algunas limitaciones por supuesto (¿no hay siempre?), Pero funcionará. Por supuesto, esto solo está previsto para operaciones de anidación. 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.

3

wow .. ¡Muchas personas no respondieron la pregunta! Lo que nadie menciona es aplicaciones multiproceso. 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 solicitud. Por ejemplo ... Tengo una mesa 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 conexión está en uso. Luego, cuando se libere, hazlo por negocios.Sin una forma clara de determinar si la conexión 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 clase que PUEDA MANTENER usando la conexión. Mucho más complejo de lo necesario

0
+1

No publique enlaces a recursos sin agregar el contenido relevante a su respuesta. Si ese enlace deja de funcionar, su respuesta sería inútil para los futuros visitantes. – Brandon

0

crear un nuevo objeto de comando con misma conexión y el nuevo lector de datos con el nuevo objeto de comando creado. Esto funcionará bien.

Cuestiones relacionadas