2010-09-03 10 views
9

Descargo de responsabilidad: Sé que IDisposable debe implementarse cuando se trata de recursos no administrados. El resto del código debe ser determinístico y hacer using (...) { } (equivalente a try {} finally { Dispose(); }) para garantizar una limpieza tan pronto como sea posible. Además, el GC será not call Dispose(), por lo que el patrón recomendado es anular el método Finalize() (en C# utilizando la sintaxis del destructor) que luego llama al Dispose(). El GC generalmente llamará al Finalize() (a menos que se haya llamado al GC.SuppressFinalize()).¿Se eliminará SqlConnection por GC?

Problema: Así que ahora que lo saqué del camino, tengo una extraña situación en la que no puedo hacer using (SqlConnection...) { } debido a un código fuera de mi control. Normalmente puedo hacer un determinista Dispose(), pero no puedo garantizarlo. Usé Reflector para desmontar SqlConnection y veo que usa Dispose(), pero a menos que esté ciego no hay finalizador/destructor (Finalize() o ~SqlConnection()). ¿Eso significa que el GC no "limpiará" (devolverá al grupo) la conexión en el caso impar que yo no pueda? No he podido encontrar nada definitivo ...

Respuesta

7

Bueno, no será eliminado, ya que la finalización no es eliminación.

Hay un finalizador en System.ComponentModel.Component, pero está suprimido en el constructor SQLConnection. Esta es una buena idea si heredas algo con un finalizador que conoces con un 100% de certeza que no necesitarás, pero una mala idea de lo contrario. En este caso, es una buena idea.

Recuerde, sin embargo, que SqlConnection es un contenedor en una conexión "real". De hecho, es muy probable que sea un contenedor de un conjunto cambiante de objetos que representan diferentes estados de conexión. Esto es parte del mecanismo que permite que la conexión "real" se agrupe de manera eficiente, ya que cada vez que llama al Open() obtiene el objeto relevante del grupo, y cada vez que llama al Close() (ya sea directamente, por Dispose() o dejando el alcance de using) lo devuelve.

Ahora, recuerde que solo los objetos que contienen directamente un recurso no administrado o algo que de otra manera no es asunto del GC, deben completarse. SqlConnection contiene un objeto que puede (según el estado de SqlConnection) ser uno que contenga un recurso no administrado (o incluso más profundo en el nido de clases). Por lo tanto, no es necesario que se complete el SqlConnection. Tenga en cuenta las tres posibles formas de un proceso abierto SqlConnection puede dejar de ser un proceso abierto SqlConnection:

  1. Close() se llama. Esto devuelve inmediatamente la conexión real al grupo (o la cierra si no hay agrupación).
  2. Dispose() se llama. Esto llama al Close() con el mismo efecto.
  3. El objeto obtiene la basura recogida.

Ahora, en el tercer caso, el objeto contiene una referencia al objeto que tiene la conexión real. También es el único objeto que lo hace. Por lo tanto, ese objeto también será basura. Si tiene un finalizador (que probablemente sí lo haga, aunque no voy a suponer que no hay más trucos inteligentes), ese finalizador hará que se coloque en la cola del finalizador, y finalmente se finalizará.

Si SqlConnection tenían un finaliser, los únicos efectos reales serían:

  1. Potencial de código erróneo (que trata de miembros finalisable en el código finaliser es cargada, ya que no sabe si ellos tienen finalizado).
  2. Posibilidad de ralentizar las cosas (la conexión real se finalizará de todos modos, en el mejor de los casos, estamos desacelerando la finalización y el GC).
  3. Nada que hacer aquí de todos modos (la conexión real se finalizará sin ayuda aquí).

Por lo tanto, poner un finalizador en el SqlConnection es perder sin ganar. Además, su conexión real debería eventualmente ser finalizada.

Dicho esto, todavía está lejos de ser ideal y todavía muy con probabilidades de fugas de conexiones. ¿Podría detallar con precisión por qué no puede llamar al Close() o desecharlo usted mismo? ¿Podría el código que gestiona la conexión no cerrar para usted (el objeto debe finalizar sus días en algún lugar y debe cerrarse allí)?

¿Necesita mantenerlo activo para un IDataReader o un objeto que se alimenta desde un IDataReader para permitir que se complete? En ese caso, ¿podría usar el indicador CommandBehavior.CloseConnection para que el cierre (o la eliminación) del lector cierre la conexión? Este último caso es sobre el único caso en el que puedo recordar haber tenido que permitir que una conexión deje el alcance sin eliminación.

+0

"SqlConnection contiene un objeto que puede (dependiendo del estado de SqlConnection) ser uno que contiene un recurso no administrado" - es un buen punto, no verifiqué que SqlConnection realmente contiene el recurso no administrado. Básicamente, el código que gestiona la conexión es una biblioteca externa que no se implementó correctamente. Creo que después de todo podré hacer cambios y evitar este problema por completo. –

+0

Me alegra oír eso. El asunto de la finalización del objeto correcto está muy bien, pero no lo ayudará si la finalización no ocurre con la frecuencia suficiente (e IME casi nunca lo hace, o peor, mucho peor, lo hace en prueba y no vivir). –

+0

+1 buena respuesta. Estoy un poco confundido acerca de la siguiente declaración en msdn que sugiere que SqlConnection es un recurso administrado. "No llame a Close o Dispose en una conexión, un DataReader u otro objeto ** administrado ** en el método Finalize de su clase". desde [HERE] (http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.close%28v=vs.110%29.aspx) –

Cuestiones relacionadas