2008-11-06 13 views
16

¡Por favor ayuda!.net SqlConnection no se cierra incluso dentro de un uso {}

información de fondo

Tengo una aplicación de 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 ejecuta SqlCommand antes de volver al constructor DataContext. Es decir

// In the application code 
using (DataContext db = new DataContext(GetConnection())) 
{ 
    ... Code 
} 

donde getConnection se parece a esto (he despojado a cabo la 'pelusa' de la función para que sea más fácil de leer, 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 ser un uno WPF la aplicación tiene alguna relación con el problema que estoy teniendo.

El problema que estoy teniendo

A pesar de la SqlConnection estando dispuesta junto con el DataContext en SQL Server Management Studio Todavía puedo ver un montón de 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 

Finalmente la conexión el grupo se agota y la aplicación no puede continuar.

Así que 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 impida que las conexiones se cierren y eliminen cuando se eliminan los DataContext para los que se utilizan?

Respuesta

19

De MSDN (DataContext Constructor (IDbConnection)):

Si proporciona una conexión abierta, la DataContext no cerrarla. Por lo tanto, no crea una instancia de DataContext con una conexión abierta a menos que tenga una buena razón para hacer esto.

Así que, básicamente, parece que sus conexiones están esperando a que el GC las finalice antes de que sean lanzadas. Si tienes muchos códigos que hacen esto, un enfoque podría ser superar el 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 volvería a darle (regular de datos de contexto, w/o el truco anterior) una conexión abierta todo el tiempo que estaba "usando" la conexión (lo que me permite realizar múltiples 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 
} 
0

El Dispose debería cerrar las conexiones, como MSDN señala:

Si el SqlConnection sale de 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 la agrupación de valores de agrupación de conexiones es establecida en verdadero o sí, la conexión subyacente se devuelve al conjunto de conexiones . Por otro lado, si Pooling está configurado como falso o no, la conexión subyacente al servidor está cerrada.

Supongo que su problema tiene algo que ver con GetContext().

7

El SqlProvider utilizado por el LINQ DataContext solo cierra la conexión SQL (a través de SqlConnectionManager.DisposeConnection) si fue el que lo abrió. Si le da un objeto SqlConnection ya abierto al constructor DataContext, no lo cerrará por usted. Por lo tanto, usted debe escribir:

using (SqlConnection conn = GetConnection()) 
using (DataContext db = new DataContext(conn)) 
{ 
    ... Code 
} 
1

Creo que la conexión, aunque ya no se hace referencia, está a la espera de la GC para disponer de ella totalmente.

Solución:

crear su propia clase DataContext que deriva de la auto-generado una. (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.

+0

Puede simplemente agregar una clase parcial para ampliar el contexto de datos autogenerado; no hay necesidad de subclase. –

1

Bueno, gracias por las grietas de ayuda, se ha resuelto ahora ..

Esencialmente me tomó elementos de la mayoría de las respuestas anteriores y aplicado el constructor DataContext que el anterior (que ya había sobrecargado los constructores por lo que no era' t 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 el acceso a this.Connection en el método dispose lanza una ObjectDisposedException.

¡Y lo anterior funciona tan bien como esperaba!

+0

Oye, eso es lo que dije. Debo ser inteligente. – GeekyMonkey

3

Experimenté el mismo problema al utilizar Entity Framework. Mi ObjectContext estaba alrededor de un bloque using.

Una conexión fue establecida cuando llamé SaveChanges(), pero después de la declaración using estaba fuera de su alcance, me di cuenta de que SQL Management Studio todavía tenía un "AWAITING COMMAND" para el cliente .NET SQL. Parece que esto tiene que ver con el comportamiento del proveedor ADO.NET que tiene la agrupación de conexiones activada de manera predeterminada.

De "El uso de Agrupación de conexiones con SQL Server" en MSDN (el énfasis es mío):

La agrupación de conexiones reduce el número de veces que hay que 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 al Open en una conexión, el recopilador 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 al Close en la conexión, el colector 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 siguiente llamada Open.

también ClearAllPools y ClearPool parece útil para cerrar explícitamente todas las conexiones agrupadas, si es necesario.

Cuestiones relacionadas