2012-02-16 17 views
14

Stavo guardando un po 'di codice e discuto con i colleghi di lavoro.using statement with connection.open

In particolare una sezione di codice simile a questa.

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

La domanda si avvicinò:

"Perché non spostare il cn.Open nel metodo GetConnection."

Ho detto che se "Apri" genera un'eccezione, non viene chiamata. La sua risposta è stata

"Allora, la connessione non è stata aperta, quindi perché sarebbe necessario ottenere chiuso (o smaltito)?"

per me è solo una questione di non voler sapere se o se non ho bisogno di smaltire/chiudere quindi vorrei ripetere il cn.Open nel codice invece di spostarlo nella funzione condivisa.

MA è interessante ... così ho fatto qualche lettura SQL Server Connection Pooling (ADO.NET)

Per me non è chiaro se esiste uno scenario in cui chiamare cn.Open e getta e l'eccezione in cui dispose sarebbe bisogno di essere chiamato.

Così, nel mio esempio qui sotto v'è alcuna differenza davvero tra "TestNormalWay" e "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

così basato su quello che stai dicendo .. e quello che sto guardando l'unico modo in cui Dispose verrà chiamato nel metodo WhyNotDoItTHisWay .. solo perché tu chiami Open non si disfa automaticamente della connessione ha senso. .includendo il var cn attorno all'utilizzo() {} il cn viene eliminato automaticamente assumendo anche che si riattiva anche quell'istanza .. – MethodMan

+0

Se si sta usando un ** usando ** l'istruzione non si deve dovere disfarsi di l'oggetto te stesso. Come i tuoi colleghi di lavoro sospettano se si verifichi un'eccezione quando si tenta di aprire la connessione, l'oggetto non contiene alcuna informazione da smaltire. Inoltre, come suggerito da Servy, potresti semplicemente usare un blocco try() catch() finally() se vuoi davvero essere sicuro. –

risposta

7

Il modo in cui si sta scrivendo il codice che hai sempre desidera per aprire la connessione non appena è creato così non c'è differenza.

Tuttavia è possibile aprire e chiudere una connessione più volte e in codice progettato per fare ciò c'è una grande differenza.

Potrei voler scrivere del codice dove ho una lunga routine in esecuzione che accetta un oggetto di connessione e nel tempo apre e chiude. La routine potrebbe non interessare come è stato creato l'oggetto di connessione. Quindi è un vantaggio separare l'atto di creare la connessione con l'atto di aprirlo e chiuderlo.

Per quanto riguarda la domanda di gestione delle risorse, sono d'accordo che non è un problema. La creazione di un oggetto di connessione SQL in sé non blocca alcuna risorsa, è l'atto di aprirlo che acquisisce una connessione in pool. Se l'open restituisce un'eccezione, penso sia ragionevole presumere che la connessione non sia stata aperta.

+0

È anche possibile fornire entrambi i metodi. Un "GetOpenConnection" e un "GetUnopenedConnection" in modo che sia possibile fare entrambi, ma non è necessario digitare tanto per il caso più comune. – Servy

+1

Si potrebbe, ma IMHO non è un buon progetto API. Le API dovrebbero essere minime, fornendo metodi composti come questo solo per ridurre la digitazione mi sembra un brutto modo di progettare un'API per me. Ma se vuoi un oggetto di connessione come questo, puoi scrivere un wrapper. –

+0

concordato. Se si trattasse di qualcosa di più di una singola linea di codice, potrebbe valere la pena di discuterne, ma è molto semplice essere ingombrante dell'API. Dato che l'OP ha iniziato il post in primo luogo però, apparentemente questo ha un certo valore per qualcuno quindi l'ho menzionato comunque. – Servy

1

È possibile mettere un tentativo/aggirare la chiamata .Open() nella seconda versione "pericolosa" in modo che se genera un'eccezione durante l'apertura, si sta ancora eliminando la connessione.

+0

non disporrà la connessione prima di restituirla ... quale sarebbe il punto allora? –

+0

@jsobo Hai ragione. Dovrebbe provare/prendere, non provare finalmente. Modificato. – Servy

6

Sarei propenso a restituire l'istanza dello SqlConnection senza chiamare il numero Open() nel metodo. Questo dovrebbe essere fatto se e quando è necessario. Non è necessario nella funzione di utilità.

Uno dei motivi è che alcuni oggetti richiedono uno SqlConnection, ma non necessariamente devono essere aperti.Ad esempio, uno SqlDataAdapter prende uno SqlConnection e ne gestisce l'apertura e la chiusura al suo interno. Certo, puoi aprire una connessione prima di passarla, ma poi devi essere esplicito chiudendolo.

Fare qualche passo indietro, dovrebbe essere la responsabilità del codice chiamante per gestire cosa esattamente fare con lo SqlConnection.

+0

Questa è l'opzione più sicura e dato che è ambiguo quando e se è necessario chiamare dispose, questo garantirà almeno che venga sempre chiamato (salvo che qualcuno tocchi il cavo di alimentazione ecc ...) –

+0

Ho riletto l'intero articolo di msdn e trovato questo ... "Si consiglia vivamente di chiudere sempre la connessione quando si è finito di usarla in modo che la connessione venga restituita al pool. È possibile farlo utilizzando i metodi Close o Dispose dell'oggetto Connection o aprendo tutte le connessioni all'interno di una istruzione using in C# "... ancora un'altra affermazione ambigua. Non specifica che hai effettivamente chiamato aperto sulla connessione! Solo che l'hai usato. –

0

Dopo aver raggiunto il picco interno di SqlConnection sono praticamente convinto che non importa. Un test rapido sembra confermare questa:

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 
     } 
    } 
} 

ho lasciato questo codice in corsa per un paio di minuti con un profiler allegato. Non solo è in esecuzione abbastanza veloce, ma non perde nemmeno memoria. Questo praticamente mi convince che sia ok per aprire la connessione nel metodo GetConnection.

Ovviamente tutti gli altri argomenti pubblicati qui sono ancora validi; dovresti solo aprire la connessione se la userai subito.

+0

questo non prova necessariamente nulla ... che dire se la connessione aperta funziona a volte ma fallisce gli altri ... potrebbe causare la creazione di un pool di connessioni, quindi risorse recuperate e quindi non eliminate? IE ... l'apertura non si apre effettivamente ma sotto le copertine cercando di fare un reset della connessione? –

+0

@jsobo Ok, come faresti a testarlo? Ho provato diverse combinazioni di stringhe di connessione funzionanti e non funzionanti e diversi tipi di errori. Per quanto posso dire, ogni volta che 'SqlConnection.Open' genera un'eccezione pulisce tutte le risorse sottostanti. –

+0

Non so ... ci sono molti scenari che non puoi testare. La domanda è determinata da QUANDO ottieni qualcosa che deve essere smaltito ... al momento del NUOVO o al momento dell'apertura. Se tu nuovo ... e ottieni qualcosa dal pool poi apri una connessione ripristinata su una connessione esistente e getta ... allora è meglio chiamare dispose ... se recupera dal pool e resetta sotto le copertine (solo su. Apri) e getta quindi non hai mai avuto la connessione dalla piscina e non avrebbe dovuto smaltire ... –