2008-09-26 14 views
18

Uso un patrón que se ve así más a menudo. Me pregunto si esto está bien o si hay una mejor práctica que no estoy aplicando aquí.¿Cuál es la forma correcta de garantizar que se cierra una conexión SQL cuando se lanza una excepción?

En concreto, me pregunto; en el caso de que se genere una excepción, ¿el código que tengo en el bloque finally es suficiente para garantizar que la conexión se cierra adecuadamente?

public class SomeDataClass : IDisposable 
{ 
    private SqlConnection _conn; 

    //constructors and methods 

    private DoSomethingWithTheSqlConnection() 
    { 
     //some code excluded for brevity 

     try 
     { 
      using (SqlCommand cmd = new SqlCommand(SqlQuery.CountSomething, _SqlConnection)) 
      { 
       _SqlConnection.Open(); 
       countOfSomething = Convert.ToInt32(cmd.ExecuteScalar()); 
      } 
     } 
     finally 
     { 
      //is this the best way? 
      if (_SqlConnection.State == ConnectionState.Closed) 
       _SqlConnection.Close(); 
     } 

     //some code excluded for brevity 
    } 

    public Dispose() 
    { 
     _conn.Dispose(); 
    } 
} 
+1

Uh ... ¿por qué estás mirando que la conexión ya está cerrado antes de cerrarlo? ¿Y por qué usar un miembro de la clase para almacenar la conexión, si vas a cerrarla en el único método que la usa? – Shog9

+1

spoon16 incluyó la frase "// algún código excluido por brevedad". De esto, infiero que este no es el único método que lo usa. –

+0

correcto, este es absolutamente el ejemplo más simple que pude pensar. Creo que debido a la agrupación de conexiones no es necesario tratar de almacenar un objeto SqlConnection fuera del alcance de cada método. Entonces solo voy a usar USING como otros recomendados. –

Respuesta

45

envolver su código de gestión de base de datos dentro de un "uso"

using (SqlConnection conn = new SqlConnection (...)) 
{ 
    // Whatever happens in here, the connection is 
    // disposed of (closed) at the end. 
} 
+0

También tenga en cuenta que a menos que específicamente lo desactive en la cadena de conexión, .NET agrupa las conexiones de manera predeterminada, por lo que no necesita intentar mantener la conexión para reutilizarla. –

1

Dado que está utilizando IDisposables de todos modos. Puede usar la palabra clave 'using', que es básicamente equivalente a llamar a disponer en un bloque finally, pero se ve mejor.

2

Supongo que por "_SqlConnection.State == ConnectionState.Closed" significó! =.

Esto sin duda funcionará. Creo que es más habitual contener el objeto de conexión dentro de una sentencia using, pero lo que tienes es bueno si quieres reutilizar el mismo objeto de conexión por alguna razón.

Sin embargo, una cosa que definitivamente debería cambiar es el método Dispose(). No debe hacer referencia al objeto de conexión en disposición, ya que puede que ya se haya finalizado en ese punto. En su lugar, debe seguir el patrón de Disposición recomendado.

-4

podría sugerir esto:


    class SqlOpener : IDisposable 
    { 
     SqlConnection _connection; 

     public SqlOpener(SqlConnection connection) 
     { 
      _connection = connection; 
      _connection.Open(); 

     } 

     void IDisposable.Dispose() 
     { 
      _connection.Close(); 
     } 
    } 

    public class SomeDataClass : IDisposable 
    { 
     private SqlConnection _conn; 

     //constructors and methods 

     private void DoSomethingWithTheSqlConnection() 
     { 
      //some code excluded for brevity 
      using (SqlCommand cmd = new SqlCommand("some sql query", _conn)) 
      using(new SqlOpener(_conn)) 
      { 
       int countOfSomething = Convert.ToInt32(cmd.ExecuteScalar()); 
      } 
      //some code excluded for brevity 
     } 

     public void Dispose() 
     { 
      _conn.Dispose(); 
     } 
    } 

Espero que ayude :)

+0

-1: ¡no funciona! El SqlCommand todavía tiene una referencia nula. Por favor, no lo arregles, simplemente sigue la solución estándar que está arriba de – Joe

+0

Err, realmente no tengo ningún problema con ejecutar esto suponiendo que el código * alrededor de * realmente crea una conexión. Ver, si "_conn" no se inicializa correctamente, por supuesto, no funcionará. –

1

poner el código estrecha relación dentro de un bloque "Finalmente", como se muestra. Finalmente, los bloques se ejecutan antes de lanzar la excepción. Usar un bloque "usar" funciona igual de bien, pero el método explícito "Finalmente" me parece más claro.

Las declaraciones de uso son anticuadas para muchos desarrolladores, pero los desarrolladores más jóvenes pueden no saberlo.

+0

las declaraciones de "uso" son muy claras para cualquiera que esté familiarizado con C#.Estoy usando Java profesionalmente en este momento, y el equipaje extra de try/finally en comparación con "usar" es una verdadera tarea. La instrucción "using" es el medio idiomático de manejo de recursos en C#. –

+0

Es suficiente, pero me parece que incluso cuando sé lo que hace algo como "usar", ver el código me ayuda a visualizar la acción. Además, no soy el único que ve mi código. –

8

.Net Framework mantiene un grupo de conexiones por un motivo. ¡Confía en ello! :) No tiene que escribir tanto código solo para conectarse a la base de datos y liberar la conexión.

Puede simplemente usar la instrucción 'using' y tener la seguridad de que 'IDBConnection.Release()' cerrará la conexión por usted.

Las 'soluciones' altamente elaboradas tienden a dar como resultado un código defectuoso. Simple es mejor.

5

MSDN Docs hacer esta bastante claro ...

  • El método Close deshace las transacciones pendientes. A continuación, libera la conexión al grupo de conexiones o cierra la conexión si la agrupación de conexiones está deshabilitada.

Probablemente no haya (y no desee) deshabilitar la agrupación de conexiones, por lo que la agrupación finalmente gestiona el estado de la conexión después de llamar a "Cerrar". Esto podría ser importante ya que puede confundirse mirando desde el lado del servidor de la base de datos en todas las conexiones abiertas.


  • Una aplicación puede llamar a cerrar más de una vez. No se genera ninguna excepción.

¿Por qué molestarse en la prueba de Cerrado? Simplemente llame a Close().


  • Cerrar y botar son funcionalmente equivalentes.

Es por esto que un utilizando el bloque da como resultado una conexión cerrada. usando llamadas Eliminar para usted.


  • No llamar Cerrar o disponer de una conexión, un DataReader, o cualquier otro objeto administrado en el método Finalize de la clase.

Sugerencia importante de seguridad. Gracias, Egon.

1

Ver esta pregunta para la respuesta:

Close and Dispose - which to call?

Si su vida es una sola conexión de llamada a un método, utilice la función using de la lengua para asegurar la limpieza adecuada de la conexión. Mientras que un bloque try/finally es funcionalmente el mismo, requiere más código y el IMO es menos legible. No es necesario verificar el estado de la conexión, puede llamar al Dispose independientemente y se encargará de limpiar la conexión.

Si la vida útil de su conexión corresponde a la vida útil de una clase contenedora, implemente IDisposable y limpie la conexión en Dispose.

0

hay necesidad de un try..finally en torno a un "uso", el uso de ES un try..finally

Cuestiones relacionadas