c# - example - sqlconnection vb.net ejemplo
.net SqlConnection no se cierra incluso dentro de un uso{} (6)
Bueno, gracias por la ayuda de los chicos, ahora se ha resuelto ...
Esencialmente tomé elementos de la mayoría de las respuestas anteriores e implementé el constructor DataContext como se indicó anteriormente (ya había sobrecargado los constructores, por lo que no fue un gran cambio).
// Variable for storing the connection passed to the constructor
private System.Data.SqlClient.SqlConnection _Connection;
public DataContext(System.Data.SqlClient.SqlConnection Connection) : base(Connection)
{
// Only set the reference if the connection is Valid and Open during construction
if (Connection != null)
{
if (Connection.State == System.Data.ConnectionState.Open)
{
_Connection = Connection;
}
}
}
protected override void Dispose(bool disposing)
{
// Only try closing the connection if it was opened during construction
if (_Connection!= null)
{
_Connection.Close();
_Connection.Dispose();
}
base.Dispose(disposing);
}
La razón para hacer esto en lugar de algunas de las sugerencias anteriores es que accediendo a esto. La this.Connection
en el método de eliminación arroja una ObjectDisposedException .
¡Y lo anterior funciona tan bien como esperaba!
¡Por favor ayuda!
Información de fondo
Tengo una aplicación WPF que accede a una base de datos de SQL Server 2005. La base de datos se ejecuta localmente en la máquina en la que se ejecuta la aplicación.
Donde quiera que use el Linq DataContext utilizo una instrucción {} que utiliza, y paso el resultado de una función que devuelve un objeto SqlConnection que se ha abierto y se ejecutó con un SqlCommand antes de volver al constructor DataContext. Es decir
// In the application code
using (DataContext db = new DataContext(GetConnection()))
{
... Code
}
donde getConnection se ve así (he eliminado la "pelusa" de la función para que sea más legible, pero no hay ninguna funcionalidad adicional que falta).
// Function which gets an opened connection which is given back to the DataContext constructor
public static System.Data.SqlClient.SqlConnection GetConnection()
{
System.Data.SqlClient.SqlConnection Conn = new System.Data.SqlClient.SqlConnection(/* The connection string */);
if ( Conn != null )
{
try
{
Conn.Open();
}
catch (System.Data.SqlClient.SqlException SDSCSEx)
{
/* Error Handling */
}
using (System.Data.SqlClient.SqlCommand SetCmd = new System.Data.SqlClient.SqlCommand())
{
SetCmd.Connection = Conn;
SetCmd.CommandType = System.Data.CommandType.Text;
string CurrentUserID = System.String.Empty;
SetCmd.CommandText = "DECLARE @B VARBINARY(36); SET @B = CAST(''" + CurrentUserID + "'' AS VARBINARY(36)); SET CONTEXT_INFO @B";
try
{
SetCmd.ExecuteNonQuery();
}
catch (System.Exception)
{
/* Error Handling */
}
}
return Conn;
}
No creo que la solicitud sea un WPF tenga ninguna relación con el problema que estoy teniendo.
El problema que estoy teniendo
A pesar de que SqlConnection está dispuesto junto con DataContext en el estudio Sql Server Management, aún puedo ver muchas conexiones abiertas con:
status : ''Sleeping''
command : ''AWAITING COMMAND''
last SQL Transact Command Batch : DECLARE @B VARBINARY(36); SET @B = CAST(''GUID'' AS VARBINARY(36)); SET CONTEXT_INFO @B
Eventualmente, el grupo de conexiones se agota y la aplicación no puede continuar.
Por lo tanto, solo puedo concluir que de alguna manera ejecutar SQLCommand para configurar Context_Info significa que la conexión no se elimina cuando se elimina el DataContext.
¿Alguien puede detectar algo obvio que impediría que las conexiones se cierren y se eliminen cuando se eliminen los DataContext para los que fueron utilizados?
Creo que la conexión, aunque ya no se hace referencia, está esperando que el GC se deshaga de ella por completo.
Solución:
Crea tu propia clase DataContext que se deriva del generado automáticamente. (cambie el nombre de la base para que no tenga que cambiar ningún otro código).
En su DataContext derivado, agregue una función Dispose (). En eso - desecha la conexión interna.
Desde MSDN ( DataContext Constructor (IDbConnection)
):
Si proporciona una conexión abierta, DataContext no la cerrará. Por lo tanto, no instale un DataContext con una conexión abierta a menos que tenga una buena razón para hacerlo.
Así que, básicamente, parece que tus conexiones esperan que GC las finalice antes de que sean lanzadas. Si tienes muchos códigos que hacen esto, un enfoque podría ser sobreponer Dispose()
en la clase parcial del contexto de datos, y cerrar la conexión; ¡solo asegúrate de documentar que el contexto de datos asume la propiedad de la conexión!
protected override void Dispose(bool disposing)
{
if(disposing && this.Connection != null && this.Connection.State == ConnectionState.Open)
{
this.Connection.Close();
this.Connection.Dispose();
}
base.Dispose(disposing);
}
Personalmente, me encantaría darle (contexto de datos regular, sin el hack anterior) una conexión abierta, siempre y cuando estuviese "usando" la conexión (lo que me permite realizar varias operaciones) - es decir,
using(var conn = GetConnection())
{
// snip: some stuff involving conn
using(var ctx = new FooContext(conn))
{
// snip: some stuff involving ctx
}
// snip: some more stuff involving conn
}
El Dispose
debe cerrar las conexiones, como señala MSDN :
Si SqlConnection queda fuera del alcance, no se cerrará. Por lo tanto, debe cerrar explícitamente la conexión llamando a Close o Dispose. Close y Dispose son funcionalmente equivalentes. Si el conjunto de valores de agrupación de conexiones se establece en verdadero o sí, la conexión subyacente se devuelve al grupo de conexiones. Por otro lado, si Pooling está configurado en falso o no, la conexión subyacente al servidor está cerrada.
Supongo que tu problema tiene algo que ver con GetContext()
.
El SqlProvider
utilizado por LINQ DataContext
solo cierra la conexión SQL (a través de SqlConnectionManager.DisposeConnection
) si fue el único que la abrió. Si le da un objeto SqlConnection
ya abierto al constructor DataContext
, no lo cerrará por usted. Por lo tanto, debes escribir:
using (SqlConnection conn = GetConnection())
using (DataContext db = new DataContext(conn))
{
... Code
}
Experimenté el mismo problema al usar Entity Framework. Mi ObjectContext
estaba envuelto alrededor de un bloque de using
.
Se estableció una conexión cuando llamé a SaveChanges()
, pero después de que la instrucción using
estuvo fuera de alcance, noté que SQL Management Studio todavía tenía un "AWAITING COMMAND"
para .NET SQL Client. Parece que esto tiene que ver con el comportamiento del proveedor ADO.NET que tiene la agrupación de conexiones activada de manera predeterminada.
Desde " Uso de la agrupación de conexiones con SQL Server " en MSDN (énfasis mío):
La agrupación de conexiones reduce la cantidad de veces que se deben abrir nuevas conexiones. El pooler mantiene la propiedad de la conexión física. Gestiona las conexiones manteniendo vivo un conjunto de conexiones activas para cada configuración de conexión determinada. Cada vez que un usuario llama a
Open
en una conexión, el agrupador busca para ver si hay una conexión disponible en el grupo. Si hay una conexión agrupada disponible, la devuelve a la persona que llama en lugar de abrir una nueva conexión. Cuando la aplicación llama aClose
en la conexión, el grupo lo devuelve al conjunto agrupado de conexiones activas en lugar de cerrarlo realmente. Una vez que la conexión se devuelve al grupo, está lista para ser reutilizada en la próxima llamadaOpen
.
También ClearAllPools
y ClearPool
parecen útiles para cerrar explícitamente todas las conexiones agrupadas si es necesario.