.net-3.5 - recorrer - sql data reader vb
¿Por qué llamar al método SqlClient.SqlDataReader Close() de todos modos? (8)
Creo que todos los demás dijeron esto, pero yo quería que fuera claro:
Fuera del alcance no significa recolección de basura inmediata.
Tu aplicación necesita ''jugar bien'' en varios niveles. Cerrar las conexiones te ayuda a hacer eso. Examinemos algunos de esos niveles.
1: no confía en la recolección de basura. Idealmente, la recolección de basura no debería existir. Pero lo hace. Pero seguramente no deberías confiar en eso.
2: no mantiene las conexiones de su base de datos. Si bien las conexiones generalmente se agrupan, como has descubierto, hay un límite. Mantener esto más tiempo de lo necesario hace que tu aplicación sea la mala manzana.
3: no está generando tráfico de red. Cada conexión de base de datos es esencialmente una conexión TCP. Mantenerlo abierto probablemente genere tráfico de red a lo largo de las líneas de ¿Todavía estás allí? sí. Poco tráfico sí, pero en una red atestada esta es una mala práctica. Y SQL Server está utilizando recursos para mantener viva su conexión. Recursos que otras personas que intentan acceder a ese servidor sql podrían aprovechar mejor.
Al pensar en el acceso a la base de datos, también debe pensar en los recursos de la red. Algunas formas de obtener los datos son malas porque pueden traer cosas innecesarias durante el viaje. Las versiones anteriores de ADO eran bastante notorias para este tipo de cosas. Recuperar la información del esquema cuando solo quiere los datos. Claro, con solo unas pocas conexiones esto no es un problema. Pero desde cuándo cualquier base de datos tiene solo unas pocas conexiones. Así que piensa en esto y trata de no abusar de los recursos.
Cuando su aplicación web solo tiene 100 usuarios, es posible que no le importe. Pero ¿qué hay de 100,000? Siempre considere lo que sucede cuando escala.
¿El objeto gestionado SqlClient.SqlDataReader es .NET o no? ¿Por qué tenemos que llamar al método Close () para cerrar explícitamente una conexión abierta? ¿No debería quedarse sin alcance para un objeto así cerrar esto automáticamente? ¿No debería el recolector de basura limpiarlo de todos modos?
Por favor, ayúdame a entender cuál es la mejor práctica aquí.
He visto una pregunta relacionada aquí y además ilustra el problema que tengo con una aplicación web. El problema es que nos estábamos quedando sin conexiones. El error detallado está aquí:
Exception: System.InvalidOperationException
Message: Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
Source: System.Data
at System.Data.SqlClient.SqlConnectionPoolManager.GetPooledConnection(SqlConnectionString options, Boolean& isInTransaction)
at System.Data.SqlClient.SqlConnection.Open()
Para solucionar esto, tuve que cerrar explícitamente todos los objetos SQLDataReader.
Estoy usando .NET Framework 3.5
El Problema no es la Conexión, sino el Cursor SQL retenido por el SqlDataReader. Si intentas abrir un segundo sin cerrar el primero, lanzará una excepción.
El recurso ''administrado'' al que se hace referencia con el término ''código administrado'' es memoria. Eso es. Cualquier otro recurso escaso necesita ser envuelto con el patrón desechable, incluidas las conexiones a la base de datos.
La razón por la cual este es un problema para usted es que el recolector de basura no se ejecuta para cada objeto en el momento en que sale del alcance. Es mucho más eficiente recolectar más artículos con menos frecuencia. Entonces, si esperas a que el recolector elimine tus objetos (y sí, si implementas idisposable eventualmente lo hará) quizás mantengas varias conexiones de bases de datos abiertas mucho más tiempo de lo que crees.
También tenga en cuenta lo que ocurre cuando se lanza una excepción: nunca se sabe si la conexión se cerrará si de repente se ve forzado a salir del código de ejecución.
Como regla general en nuestra tienda, envolvemos explícitamente todas las llamadas a la base de datos en un bloque Try ... Finally, con la sección finally atrapando y cerrando las conexiones de datos. Vale la pena esforzarse un poco para ahorrarse un gran dolor de cabeza de resolución de problemas.
Una buena práctica (siempre que no esté reutilizando conexiones) es agregar el Comportamiento del comando al SqlDataReader para cerrar la conexión cuando se elimine:
SqlDataReader rdr = cmd.ExecuteReader( CommandBehavior.CloseConnection );
Agregar esto asegurará que la conexión a la base de datos se cierre cuando el objeto SqlDataReader se cierra (o se recolecta basura).
Sin embargo, como dije anteriormente, no desea hacer esto si planea volver a utilizar la conexión de la base de datos para otra operación dentro del mismo método.
Claro, se recogerá cuando salga del alcance (si no hay otras referencias). Cuando se recopila, se cerrará a través de su método Dispose (). Sin embargo, nunca se sabe realmente cuándo el GC va a desmantelar las cosas; si no cierra sus lectores, se quedará rápidamente sin conexiones disponibles a la base de datos.
Otras lecturas
- Explicación del grupo de conexiones ADO.NET de O''Reilly
- La recuperación de datos de Microsoft utilizando un DataReader tiene una descripción general de DataReaders.
~ William Riley-Land
@teniente Frost
Como regla general en nuestra tienda, envolvemos explícitamente todas las llamadas a la base de datos en un bloque Try ... Finally, con la sección finally atrapando y cerrando las conexiones de datos. Vale la pena esforzarse un poco para ahorrarse un gran dolor de cabeza de resolución de problemas.
Tengo una regla similar, pero requiero que los objetos que implementan IDisposable usen el bloque ''usar''.
using (SqlConnection conn = new SqlConnection(conStr))
{
using (SqlCommand command = new SqlCommand())
{
// ETC
}
}
El uso de llamadas al bloque Dispose inmediatamente al salir del alcance, incluso con una excepción.
Si no lo cierra de manera explícita, se queda allí esperando que el recolector de basura lo "recolecte" ... Solo después de que eso suceda se libera de nuevo al grupo de conexiones de ADO.Net para ser reutilizado por otra Connection.Open solicitar, por lo tanto, durante todo el tiempo intermedio, cualquier otra solicitud de conexión deberá crear una nueva, incluso si hay una muy buena que se encuentre allí sin usar y que pueda usarse ...
Dependiendo de cuánto tiempo transcurra antes de que se ejecute el GC, y cuántas solicitudes de Base de datos se están ejecutando, podría quedarse sin conexiones disponibles a la base de datos.
Pero hay un parámetro opcional en el método Command.ExecuteReader (), llamado CommandBehavior. Esta es una enumeración, con valores: CommandBehavior.Default, CommandBehavior.SingleResult, CommandBehavior.SchemaOnly, CommandBehavior.KeyInfo, CommandBehavior.SingleRow, CommandBehavior.SequentialAccess y CommandBehavior.CloseConnection
Esta enumeración tiene un atributo FlagsAttribute que permite una combinación bit a bit de sus valores de miembro. Es el último valor, (CommandBehavior.CloseConnection) que es relevante aquí. Le dice al objeto Command que cierre la conexión cuando el lector de datos esté cerrado. http://msdn.microsoft.com/en-us/library/system.data.commandbehavior.aspx
Desafortunadamente, el valor predeterminado NO es cerrar la conexión cuando el lector de datos está cerrado, pero puede, (y debe) pasar este parámetro como CommandBehavior.CloseConnection cuando desee que su método libere la conexión de nuevo al grupo inmediatamente cuando haya terminado con eso...