in un blocco "using" è chiuso un SqlConnection al ritorno o eccezione?


136

Prima domanda:
dire che ho

using (SqlConnection connection = new SqlConnection(connectionString))
{
    connection.Open();

    string storedProc = "GetData";
    SqlCommand command = new SqlCommand(storedProc, connection);
    command.CommandType = CommandType.StoredProcedure;
    command.Parameters.Add(new SqlParameter("@EmployeeID", employeeID));

    return (byte[])command.ExecuteScalar();
}

La connessione viene chiusa? Perché tecnicamente non arriviamo mai all'ultimo }come returnprima.

Seconda domanda:
questa volta ho:

try
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        int employeeID = findEmployeeID();

        connection.Open();
        SqlCommand command = new SqlCommand("UpdateEmployeeTable", connection);
        command.CommandType = CommandType.StoredProcedure;
        command.Parameters.Add(new SqlParameter("@EmployeeID", employeeID));
        command.CommandTimeout = 5;

        command.ExecuteNonQuery();
    }
}
catch (Exception) { /*Handle error*/ }

Ora, dì da qualche parte nel che tryabbiamo un errore e viene catturato. La connessione viene ancora chiusa? Perché di nuovo, saltiamo il resto del codice in trye andiamo direttamente catchall'istruzione.

Sto pensando in modo troppo lineare su come usingfunziona? cioè viene Dispose()semplicemente chiamato quando lasciamo l' usingambito?

Risposte:


178
  1. Sì.

Ad ogni modo, quando si esce dal blocco using (per completamento con esito positivo o per errore), questo viene chiuso.

Anche se penso che sarebbe meglio organizzarsi in questo modo perché è molto più facile vedere cosa accadrà, anche per il nuovo programmatore di manutenzione che lo supporterà in seguito:

using (SqlConnection connection = new SqlConnection(connectionString)) 
{    
    int employeeID = findEmployeeID();    
    try    
    {
        connection.Open();
        SqlCommand command = new SqlCommand("UpdateEmployeeTable", connection);
        command.CommandType = CommandType.StoredProcedure;
        command.Parameters.Add(new SqlParameter("@EmployeeID", employeeID));
        command.CommandTimeout = 5;

        command.ExecuteNonQuery();    
    } 
    catch (Exception) 
    { 
        /*Handle error*/ 
    }
}

3
@TrueWill - Sono d'accordo. Ho appena spostato un po 'il codice per la struttura.
David,

10
Domanda: Devo anche APRIRE una connessione quando utilizzo l'istruzione Using?
Fandango68,

3
Inoltre, se stai utilizzando le transazioni, avendo l' try catchinterno usingpuoi esplicitamente .Commito le .Rollbacktransazioni nel catch. Questo è sia più leggibile che esplicito e ti consente di impegnarti se ciò ha senso dato il tipo di eccezione. (Le transazioni implicitamente eseguono il rollback conn.Closese non vengono eseguite.).
Chris,

8
@ Fernando68 Sì, devi ancora alla Openconnessione. usinggarantisce solo che Disposeviene chiamato il metodo dell'oggetto .
giovedì

Ho il ritorno ExecuteScalar dentro usando i blocchi. E quando eseguo il metodo la seconda volta è molto veloce, come se la connessione fosse aperta. Perché è così veloce la seconda volta?
prospettiva positiva

46

Sì ad entrambe le domande. L'istruzione using viene compilata in un blocco try / finally

using (SqlConnection connection = new SqlConnection(connectionString))
{
}

equivale a

SqlConnection connection = null;
try
{
    connection = new SqlConnection(connectionString);
}
finally
{
   if(connection != null)
        ((IDisposable)connection).Dispose();
}

Modifica: correzione del cast su Monouso http://msdn.microsoft.com/en-us/library/yh598w02.aspx


non è esattamente quello, ma è abbastanza vicino. la differenza esatta non è importante.
Bryan,

@Bryan non l'ha capito, puoi per favore menzionare la differenza esatta, può aiutarci a
sporgerci di

Wow, è stato un commento fatto molto tempo fa :) Sembra che ci sia stata una modifica il giorno dopo che ho fatto quel commento. Penso che sia la differenza a cui stavo pensando.
Bryan,

@Bryan Sì, ho corretto la regolazione dopo il tuo commento.
Ryan Pedersen,

17

Ecco il mio modello. Tutto il necessario per selezionare i dati da un server SQL. La connessione viene chiusa e eliminata e vengono rilevati errori nella connessione e nell'esecuzione.

string connString = System.Configuration.ConfigurationManager.ConnectionStrings["CompanyServer"].ConnectionString;
string selectStatement = @"
    SELECT TOP 1 Person
    FROM CorporateOffice
    WHERE HeadUpAss = 1 AND Title LIKE 'C-Level%'
    ORDER BY IntelligenceQuotient DESC
";
using (SqlConnection conn = new SqlConnection(connString))
{
    using (SqlCommand comm = new SqlCommand(selectStatement, conn))
    {
        try
        {
            conn.Open();
            using (SqlDataReader dr = comm.ExecuteReader())
            {
                if (dr.HasRows)
                {
                    while (dr.Read())
                    {
                        Console.WriteLine(dr["Person"].ToString());
                    }
                }
                else Console.WriteLine("No C-Level with Head Up Ass Found!? (Very Odd)");
            }
        }
        catch (Exception e) { Console.WriteLine("Error: " + e.Message); }
        if (conn.State == System.Data.ConnectionState.Open) conn.Close();
    }
}

* Revisionato: 09/11/2015 *
Come suggerito da NickG; Se troppe parentesi graffe ti danno fastidio, formatta in questo modo ...

using (SqlConnection conn = new SqlConnection(connString))
   using (SqlCommand comm = new SqlCommand(selectStatement, conn))
   {
      try
      {
         conn.Open();
         using (SqlDataReader dr = comm.ExecuteReader())
            if (dr.HasRows)
               while (dr.Read()) Console.WriteLine(dr["Person"].ToString());
            else Console.WriteLine("No C-Level with Head Up Ass Found!? (Very Odd)");
      }
      catch (Exception e) { Console.WriteLine("Error: " + e.Message); }
      if (conn.State == System.Data.ConnectionState.Open) conn.Close();
   }

Inoltre, se lavori per i giochi EA o DayBreak, puoi anche rinunciare a qualsiasi interruzione di riga perché quelli sono solo per le persone che devono tornare a guardare il tuo codice in seguito e chi se ne frega davvero? Ho ragione? Voglio dire 1 riga invece di 23 significa che sono un programmatore migliore, giusto?

using (SqlConnection conn = new SqlConnection(connString)) using (SqlCommand comm = new SqlCommand(selectStatement, conn)) { try { conn.Open(); using (SqlDataReader dr = comm.ExecuteReader()) if (dr.HasRows) while (dr.Read()) Console.WriteLine(dr["Person"].ToString()); else Console.WriteLine("No C-Level with Head Up Ass Found!? (Very Odd)"); } catch (Exception e) { Console.WriteLine("Error: " + e.Message); } if (conn.State == System.Data.ConnectionState.Open) conn.Close(); }

Accidenti ... OK. L'ho tirato fuori dal mio sistema e mi sono divertito un po '. Proseguire.


6
Sapevi che puoi impilare usando le istruzioni senza parentesi graffe aggiuntive? Elimina l'ultimo controvento, quindi posiziona le istruzioni using una accanto all'altra :)
NickG,

Si signore. Grazie. Sono consapevole, ma volevo che il mio codice mostrasse esattamente cosa stava succedendo senza usare troppe altre scorciatoie. Buona nota da aggiungere ai lettori finali però.
ShaneLS,

Perché lo usi conn.Close();alla fine? La usingdichiarazione non fa questo per te tramite lo smaltimento?
Fredrick Gauss,

Credo che lo faccia ora (da .net 3.5). All'inizio non mi era chiaro con .net 2.0, quindi ho preso l'abitudine di controllare e chiudere.
ShaneLS,

1
"significa 1 riga invece di 23 significa che sono un programmatore migliore, giusto?" Mi piaci :-D
Philipp Müller

5

Dispose viene semplicemente chiamato quando si esce dall'ambito di utilizzo. L'intenzione di "utilizzare" è offrire agli sviluppatori un modo garantito per assicurarsi che le risorse vengano smaltite.

Da MSDN :

Un'istruzione using può essere chiusa quando viene raggiunta la fine dell'istruzione using o se viene generata un'eccezione e il controllo lascia il blocco dell'istruzione prima della fine dell'istruzione.


5

Usinggenera un tentativo / finalmente attorno all'oggetto da allocare e ti chiama Dispose().

Ti risparmia il fastidio di creare manualmente il blocco try / finally e la chiamata Dispose()


3

Nel tuo primo esempio, il compilatore C # tradurrà effettivamente l'istruzione using nel seguente:

SqlConnection connection = new SqlConnection(connectionString));

try
{
    connection.Open();

    string storedProc = "GetData";
    SqlCommand command = new SqlCommand(storedProc, connection);
    command.CommandType = CommandType.StoredProcedure;
    command.Parameters.Add(new SqlParameter("@EmployeeID", employeeID));

    return (byte[])command.ExecuteScalar();
}
finally
{
    connection.Dispose();
}

Alla fine le istruzioni verranno sempre chiamate prima che ritorni una funzione e quindi la connessione sarà sempre chiusa / eliminata.

Quindi, nel tuo secondo esempio il codice sarà compilato come segue:

try
{
    try
    {
        connection.Open();

        string storedProc = "GetData";
        SqlCommand command = new SqlCommand(storedProc, connection);
        command.CommandType = CommandType.StoredProcedure;
        command.Parameters.Add(new SqlParameter("@EmployeeID", employeeID));

        return (byte[])command.ExecuteScalar();
    }
    finally
    {
        connection.Dispose();
    }
}
catch (Exception)
{
}

L'eccezione verrà rilevata nell'istruzione finally e la connessione verrà chiusa. L'eccezione non sarà rilevata dalla clausola catch esterna.


1
ottimi esempi amico, ma non sono d'accordo sul tuo ultimo commento, se si verifica un'eccezione all'interno di un blocco che verrà catturato senza problemi su qualsiasi cattura esterna, infatti l'ho provato scrivendo 2 usando blocchi all'interno di un blocco try / catch e con mia sorpresa, ho mostrato il mio messaggio di errore di eccezione che veniva dal secondo interno usando il blocco.
WhySoSerious,

1

Ho scritto due istruzioni using all'interno di un blocco try / catch e ho visto che l'eccezione veniva catturata allo stesso modo se veniva inserita nell'istruzione using interna proprio come nell'esempio di ShaneLS .

     try
     {
       using (var con = new SqlConnection(@"Data Source=..."))
       {
         var cad = "INSERT INTO table VALUES (@r1,@r2,@r3)";

         using (var insertCommand = new SqlCommand(cad, con))
         {
           insertCommand.Parameters.AddWithValue("@r1", atxt);
           insertCommand.Parameters.AddWithValue("@r2", btxt);
           insertCommand.Parameters.AddWithValue("@r3", ctxt);
           con.Open();
           insertCommand.ExecuteNonQuery();
         }
       }
     }
     catch (Exception ex)
     {
       MessageBox.Show("Error: " + ex.Message, "UsingTest", MessageBoxButtons.OK, MessageBoxIcon.Error);
     }

Indipendentemente da dove si trovi il tentativo / cattura , l'eccezione verrà rilevata senza problemi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.