2012-02-16 13 views
14

Estaba buscando un código y discutiéndolo con mis compañeros de trabajo.usando una declaración con connection.open

Específicamente una sección de código que se ve así.

[Test] 
    public void TestNormalWay() 
    { 
     using(var cn = GetConnection()) 
     { 
      cn.Open(); 
      // do stuff 
     } 
    } 

surgió la pregunta:

"por qué no mover el cn.Open en el método getConnection."

Dije que si "Abrir" arroja una excepción, no se deshabilitaría. Su respuesta fue

"¿Y qué? La conexión no se abrió entonces ¿por qué tendría que cerrarse (o desecharse) ?"

para mí es sólo una cuestión de no querer saber si o si no lo requiere desechar/cierre así que repetir el cn.Open en el código en lugar de moverlo a la función compartida.

PERO es interesante ... Así que hice una lectura en SQL Server Connection Pooling (ADO.NET)

Para mí no está claro si existe un escenario en el que llamar cn.Open y se lanza una excepción y en disponer lo haría necesita ser llamado.

Así que en mi siguiente ejemplo ¿Hay alguna diferencia realmente entre "TestNormalWay" y "WhyNotDoItThisWay"

protected static DbConnection GetConnection() 
    { 
     DbConnection cn = new SqlConnection("SomeConnecitonstring... "); 
     return cn; 
    } 

    protected static DbConnection GetConnectionDangerousVersion() 
    { 
     DbConnection cn = new SqlConnection("SomeConnecitonstring... "); 
     cn.Open(); // this will throw.. .dispose not called 
     return cn; 
    } 

    [Test] 
    public void TestNormalWay() 
    { 
     using(var cn = GetConnection()) 
     { 
      cn.Open(); 
      // do stuff 
     } 
    } 

    [Test] 
    public void WhyNotDoItThisWay() 
    { 
     using(var cn = GetConnectionDangerousVersion()) 
     { 
      // do stuff 
     } 
    } 
+0

por lo que dices ... y lo que estoy viendo es la única forma en que Dispose se invocará en el método WhyNotDoItTHisWay. Solo porque llame a Open no descarta automáticamente la conexión si tiene sentido. . envolviendo la var cn alrededor de using() {} el cn se elimina automáticamente asumiendo que usted también ha actualizado esa instancia .. – MethodMan

+0

Si está usando una declaración ** using ** no debería tener que deshacerse de el objeto mismo. Como sus compañeros de trabajo sospechan que ocurre una excepción al intentar abrir la conexión, el objeto no contiene información para deshacerse de ella. Además, como sugiere Servy, simplemente puedes usar un bloque try() catch() finally() si realmente quieres estar seguro. –

Respuesta

7

La forma en que está escrito el código que siempre desea para abrir la conexión tan pronto como sea creado así que no hay diferencia.

Sin embargo, puede abrir y cerrar una conexión varias veces y en el código diseñado para hacerlo hay una gran diferencia.

Es posible que desee escribir un código donde tengo una rutina de ejecución larga que toma un objeto de conexión y con el tiempo lo abre y lo cierra. Es posible que a la rutina no le importe cómo se creó el objeto de conexión. Por lo tanto, es una ventaja separar el acto de crear la conexión con el acto de abrirlo y cerrarlo.

Con respecto a la pregunta sobre la gestión de recursos, estoy de acuerdo en que no es un problema. Crear un objeto de conexión SQL en sí mismo no bloquea ningún recurso, es el acto de abrirlo el que adquiere una conexión agrupada. Si el abierto devuelve una excepción, creo que es razonable suponer que la conexión no se abrió.

+0

También podría proporcionar ambos métodos. Una "GetOpenConnection" y una "GetUnopenedConnection" para que pueda hacerlo, pero no necesita escribir tanto para el caso más común. – Servy

+1

Podría, pero en mi humilde opinión no es un buen diseño de API. Las API deben ser mínimas, proporcionar métodos compuestos como este solo para reducir el tipeo es una forma desagradable de diseñar una API para mí. Pero si quiere un objeto de conexión así, puede escribir un contenedor. –

+0

De acuerdo. Si fuera algo más que una sola línea corta de código, podría valer la pena discutirlo, pero vale la pena simplemente desordenar la API. Dado que el OP comenzó la publicación en primer lugar, aparentemente esto tiene algún valor para alguien, así que lo mencioné de todos modos. – Servy

1

Puede poner try/catch alrededor de la llamada .Open() en la segunda versión "peligrosa" para que, si arroja una excepción al abrir, siga desechando la conexión.

+0

no sería eso disponer la conexión antes de devolverlo ... ¿cuál sería el punto, entonces? –

+0

@jsobo Estás en lo correcto. Debería ser try/catch, no intentar finalmente. Editado – Servy

6

Me gustaría simplemente devolver la instancia del SqlConnection sin llamar al Open() en su método. Eso debe hacerse siempre y cuando sea necesario. No es necesario en su función de utilidad.

Una de las razones es que hay algunos objetos que requieren un SqlConnection, pero no necesariamente los necesitan para abrirse.Por ejemplo, un SqlDataAdapter toma un SqlConnection, y maneja la apertura y el cierre de él dentro de sí mismo. Claro, puedes abrir una conexión antes de pasarla, pero luego tienes que ser explícito al cerrarla.

Retrocediendo unos pasos, debería ser responsabilidad del código de llamada manejar exactamente qué hacer con el SqlConnection.

+0

Esta es la opción más segura y dado que es ambiguo cuándo y si debe realizar una llamada a disposición, al menos se asegurará de que siempre se llame (salvo que alguien tire del cable de alimentación, etc.) –

+0

Releí todo el artículo msdn y encontró esto ... "Recomendamos encarecidamente que siempre cierre la conexión cuando haya terminado de usarla para que la conexión se devuelva al grupo. Puede hacerlo utilizando los métodos Cerrar o Eliminar del objeto Conexión, o mediante abriendo todas las conexiones dentro de una declaración using en C# "... otra vez, otra declaración ambigua. ¡No especifica que realmente llamaste para abrir la conexión! Solo que lo usaste. –

0

Después de llegar a las partes internas de SqlConnection estoy bastante convencido de que realmente no importa. Una prueba rápida parece confirmar esto:

static void Main(string[] args) 
{ 
    Func<SqlConnection> getConnection = 
      () => 
       { 
        var connection = 
        new SqlConnection(
         "Initial Catalog=myDatabase;Server=(local);Username=bogus;password=blah;Connect Timeout=10;"); 

        connection.Open(); 
        return connection; 
       }; 

    while(true) 
    { 
     try 
     { 
     using(var connection = getConnection()) 
     { 
      var cmd = new SqlCommand("SELECT 1", connection) {CommandType = CommandType.Text}; 
      cmd.ExecuteNonQuery(); 
     } 
     } 
     catch (Exception) 
     { 
     // ignore exception 
     } 
    } 
} 

Dejé este código ejecutándose durante un par de minutos con un generador de perfiles adjunto. No solo está funcionando bastante rápido, sino que también no está perdiendo memoria. Esto me convence bastante de que está bien abrir la conexión en su método GetConnection.

Por supuesto, todos los demás argumentos publicados aquí siguen siendo válidos; solo deberías abrir la conexión si vas a usarla de inmediato.

+0

esto no prueba necesariamente nada ... ¿qué pasa si la conexión abierta funciona a veces pero falla en otras ... eso causaría que se creara un grupo de conexiones, luego se recuperaran los recursos y luego no se eliminen? IE ... ¿la apertura no se está abriendo pero debajo de las coberturas tratando de hacer un restablecimiento de la conexión? –

+0

@jsobo Ok, ¿cómo lo probarías? Probé diferentes combinaciones de cadenas de conexión que funcionan y que no funcionan, y diferentes tipos de errores. Por lo que puedo decir, cada vez que 'SqlConnection.Open' lanza una excepción, limpia los recursos subyacentes. –

+0

No lo sé ... hay muchos escenarios que simplemente no puedes probar. La pregunta realmente está determinada por CUÁNDO obtiene algo que debe eliminarse ... en el momento de NUEVO o en el momento de ABRIR. Si eres nuevo ... y obtienes algo de la agrupación, entonces abre una conexión restablecida a una conexión existente y arroja ... entonces es mejor que llames a deshacerte ... si lo hace, recupera de la agrupación y restablece bajo las cubiertas (solo on .Open) y throws entonces nunca obtuviste la conexión del grupo y no tendrías que desechar ... –

Cuestiones relacionadas