Domanda

Sto solo guardando la dichiarazione using, ho sempre saputo cosa fa ma fino ad ora non ho provato a usarla, ho escogitato il seguente codice:

 using (SqlCommand cmd = 
     new SqlCommand(reportDataSource, 
         new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString)))
 {
     cmd.CommandType = CommandType.StoredProcedure;
     cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
     cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
     cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
     cmd.Connection.Open();

     DataSet dset = new DataSet();
     new SqlDataAdapter(cmd).Fill(dset);
     this.gridDataSource.DataSource = dset.Tables[0];
 }

Questo sembra funzionare, ma c'è qualche punto in questo dal momento che, per quanto posso dire, avrei ancora bisogno di racchiuderlo in un blocco try catch per rilevare errori imprevisti, ad es. sql server inattivo. Mi sto perdendo qualcosa?

Per quanto posso attualmente vedere, mi impedisce di chiudere e eliminare cmd, ma ci saranno più righe di codice a causa del tentativo di cattura ancora necessario.

È stato utile?

Soluzione

Questo codice dovrebbe essere il seguente per garantire la chiusura tempestiva della connessione. Chiudere solo il comando non chiude la connessione:

using (SqlConnection con = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
using (SqlCommand cmd = new SqlCommand(reportDataSource, con))
         {
             cmd.CommandType = CommandType.StoredProcedure;
             cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
             cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
             cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
             cmd.Connection.Open();

             DataSet dset = new DataSet();
             new SqlDataAdapter(cmd).Fill(dset);
             this.gridDataSource.DataSource = dset.Tables[0];
         }

Per rispondere alla tua domanda, puoi fare lo stesso in un blocco finally, ma questo ambisce bene al codice e ti assicura di ricordarti di ripulire.

Altri suggerimenti

Quando eseguo IO lavoro, codice per aspettarmi un'eccezione.

SqlConnection conn = null;
SqlCommand cmd = null;

try
{
    conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString)
    cmd = new SqlCommand(reportDataSource, conn);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;

        conn.Open(); //opens connection

    DataSet dset = new DataSet();
    new SqlDataAdapter(cmd).Fill(dset);
    this.gridDataSource.DataSource = dset.Tables[0];
}
catch(Exception ex)
{
    Logger.Log(ex);
    throw;
}
finally
{
    if(conn != null)
        conn.Dispose();

        if(cmd != null)
        cmd.Dispose();
}

Modifica: Per essere esplicito, evito qui il blocco usando perché ritengo sia importante accedere a situazioni come questa. L'esperienza mi ha insegnato che non si sa mai che tipo di strana eccezione potrebbe apparire. Accedere a questa situazione potrebbe aiutarti a rilevare un deadlock o trovare dove una modifica dello schema ha un impatto su una parte poco utilizzata e poco testata della tua base di codice o su un numero qualsiasi di altri problemi.

Modifica 2: Si può sostenere che un blocco usando potrebbe avvolgere un tentativo / cattura in questa situazione, e questo è completamente valido e funzionalmente equivalente. Questo si riduce davvero alle preferenze. Vuoi evitare l'ulteriore nidificazione a spese della gestione del tuo smaltimento? Oppure incorri nella nidificazione extra per avere lo smaltimento automatico. Sento che il primo è più pulito, quindi lo faccio in questo modo. Tuttavia, non riscrivo quest'ultimo se lo trovo nella base di codice in cui sto lavorando.

Modifica 3: Vorrei davvero che MS avesse creato una versione più esplicita di using () che rendesse più intuitivo ciò che stava realmente accadendo e che avesse dato maggiore flessibilità in questo caso. Considera il seguente codice immaginario:

SqlConnection conn = null;
SqlCommand cmd = null;

using(conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString),
          cmd = new SqlCommand(reportDataSource, conn)
{
    conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString);
    cmd = new SqlCommand(reportDataSource, conn);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
        cmd.Open();

    DataSet dset = new DataSet();
    new SqlDataAdapter(cmd).Fill(dset);
    this.gridDataSource.DataSource = dset.Tables[0];
}
catch(Exception ex)
{
    Logger.Log(ex);
    throw;
}

Un'istruzione using crea semplicemente una chiamata try / finally con Dispose () in finally. Perché non dare allo sviluppatore un modo unificato di smaltimento e gestione delle eccezioni?

In questo caso potrebbe non esserci alcun vantaggio nell'utilizzare un'istruzione using se hai intenzione di avere un try / catch / < codice> infine blocca comunque. Come sapete, l'istruzione using è zucchero sintattico per un try / infine che elimina l'oggetto IDisposable . Se hai intenzione di avere il tuo prova / finalmente , puoi certamente fare tu stesso il Dispose .

Questo si riduce in gran parte allo stile: il tuo team potrebbe essere più a suo agio con usando o usando può rendere il codice più pulito.

Ma, se la piastra di cottura che si nasconde l'istruzione using è comunque lì, vai avanti e gestisci le cose da solo se è la tua preferenza.

Se il tuo codice è simile al seguente:

using (SqlCommand cmd = new SqlCommand(...))
{
  try
  {
    /* call stored procedure */
  }
  catch (SqlException ex)
  {
    /* handles the exception. does not rethrow the exception */
  }
}

Quindi lo refactoring per usare try .. catch .. finalmente invece.

SqlCommand cmd = new SqlCommand(...)
try
{
  /* call stored procedure */
}
catch (SqlException ex)
{
  /* handles the exception and does not ignore it */
}
finally
{
   if (cmd!=null) cmd.Dispose();
}

In questo scenario, gestirò l'eccezione, quindi non ho altra scelta che aggiungere quel try..catch, potrei anche inserire la clausola finally e salvarmi un altro livello di annidamento. Nota che devo fare qualcosa nel blocco catch e non semplicemente ignorare l'eccezione.

Elaborando ciò che ha detto Chris Ballance, la specifica C # (ECMA-334 versione 4) sezione 15.13 afferma "Un'istruzione using è tradotta in tre parti: acquisizione, utilizzo e smaltimento. L'uso della risorsa è implicitamente racchiuso in un'istruzione try che include una clausola finally. Questa clausola infine dispone della risorsa. Se viene acquisita una risorsa nulla, non viene effettuata alcuna chiamata a Dispose e non viene generata alcuna eccezione. & Quot;

La descrizione è vicina a 2 pagine - vale la pena leggerla.

Nella mia esperienza, SqlConnection / SqlCommand può generare errori in così tanti modi che è quasi necessario gestire le eccezioni generate più che gestire il comportamento previsto. Non sono sicuro che vorrei la clausola using qui, poiché vorrei essere in grado di gestire da solo il caso di risorsa nulla.

l'utilizzo non riguarda la cattura di eccezioni. Si tratta di smaltire correttamente le risorse che sono al di fuori della vista del Garbage Collector.

un problema con " utilizzando " è che non gestisce le eccezioni. se i progettisti di " utilizzano " aggiungerebbe "cattura" opzionalmente alla sua sintassi come sotto lo pseudocodice, sarebbe molto più utile:

using (...MyDisposableObj...)
{

   ... use MyDisposableObj ...

catch (exception)

   ... handle exception ...

}

it could even have an optional "finally" clause to cleanup anything other than the "MyDisposableObj" allocated at the beginning of the "using" statement... like:

using (...MyDisposableObj...)
{

   ... use MyDisposableObj ...
   ... open a file or db connection ...

catch (exception)

   ... handle exception ...

finally

   ... close the file or db connection ...

}

ancora non sarà necessario scrivere il codice per disporre di MyDisposableObj b / c che sarebbe gestito da usando ...

Come ti piace?

Sì, dovresti comunque rilevare eccezioni. Il vantaggio del blocco using è che stai aggiungendo ambito al tuo codice. Stai dicendo, " All'interno di questo blocco di codice fai alcune cose e quando arriva alla fine, chiudi e getta le risorse "

Non è del tutto necessario, ma definisce le tue intenzioni a chiunque usi il tuo codice e aiuta anche a non lasciare le connessioni, ecc. aperte per errore.

Ci sono molte ottime risposte qui, ma non credo che sia stato ancora detto.

Non importa quale ... il " Dispose " il metodo SARÀ chiamato sull'oggetto in " usando " bloccare. Se inserisci una dichiarazione di reso o generi un errore, il messaggio "Dispose" verrà chiamato.

Esempio:

Ho creato una classe chiamata " MyDisposable " ;, e implementa IDisposable e fa semplicemente una Console.Write. sempre scrive sulla console anche in tutti questi scenari:

using (MyDisposable blah = new MyDisposable())
{
    int.Parse("!"); // <- calls "Dispose" after the error.

    return; // <-- calls Dispose before returning.
}

L'istruzione using viene effettivamente cambiata in un blocco try / finally dal compilatore in cui il parametro del blocco using è eliminato fintanto che implementa l'interfaccia IDisposable. Oltre a garantire che gli oggetti specificati siano correttamente disposti quando cadono fuori dall'ambito di applicazione, non c'è davvero alcun errore di acquisizione acquisito utilizzando questo costrutto.

Come menzionato in precedenza TheSoftwareJedi , è necessario assicurarsi che gli oggetti SqlConnection e SqlCommand siano eliminati correttamente. Impilare entrambi in un singolo blocco usando è un po 'disordinato e potrebbe non fare quello che pensi che faccia.

Inoltre, prestare attenzione all'utilizzo del blocco try / catch come logica. È un odore di codice per il quale il mio naso è particolarmente antipatico e spesso usato dai neofiti o da quelli di noi che hanno molta fretta di rispettare una scadenza.

Cordiali saluti, in questo esempio specifico, poiché si utilizza una connessione ADO.net e un oggetto Command, tenere presente che l'istruzione using esegue appena Command.Dispose e Connection.Dispose () che non chiudono effettivamente l'oggetto connessione, ma semplicemente lo rilascia nuovamente nel pool di connessioni ADO.net per essere riutilizzato dalla connessione.open successiva ... il che è positivo, e la cosa assolutamente corretta da fare, in caso contrario, la connessione rimarrà inutilizzabile fino a quando il Garbage Collector non lo rilascia nuovamente nel pool, il che potrebbe non avvenire fino a numerose altre richieste di connessione, che altrimenti sarebbero costrette a creare nuove connessioni anche se ce n'è una inutilizzata in attesa di essere garbage collection.

Prenderei la mia decisione su quando e quando non usare l'istruzione using in base alla risorsa con cui mi sto occupando. Nel caso di una risorsa limitata, come una connessione ODBC, preferirei utilizzare T / C / F in modo da poter registrare errori significativi nel momento in cui si sono verificati. Consentire agli errori del driver del database di risalire al client e potenzialmente perdersi nel wrapping delle eccezioni di livello superiore non è ottimale.

T / C / F ti dà la tranquillità che la risorsa viene gestita nel modo desiderato. Come alcuni hanno già accennato, l'istruzione using non fornisce la gestione delle eccezioni, ma garantisce solo che la risorsa sia distrutta. La gestione delle eccezioni è una struttura linguistica sottoutilizzata e sottovalutata che è spesso la differenza tra il successo e il fallimento di una soluzione.

Se il chiamante della tua funzione è responsabile della gestione di eventuali eccezioni, l'istruzione using è un buon modo per garantire che le risorse vengano ripulite indipendentemente dal risultato.

Ti consente di posizionare il codice di gestione delle eccezioni ai limiti di layer / assembly e aiuta a evitare che altre funzioni diventino troppo disordinate.

Naturalmente, dipende davvero dai tipi di eccezioni generati dal tuo codice. A volte dovresti usare try-catch-finally piuttosto che un'istruzione using. La mia abitudine è di iniziare sempre con un'istruzione using per IDisposables (o avere classi che contengono IDisposables implementano anche l'interfaccia) e aggiungere try-catch-finally secondo necessità.

Quindi, in pratica, " usando " è lo stesso di " Try / catch / finally " solo molto più flessibile per la gestione degli errori.

Correzione minore dell'esempio: SqlDataAdapter deve anche essere istanziato in un'istruzione usando :

using (SqlConnection con = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
using (SqlCommand cmd = new SqlCommand(reportDataSource, con))
{
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
    con.Open();

    DataSet dset = new DataSet();
    using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
    {
        adapter.Fill(dset);
    }
    this.gridDataSource.DataSource = dset.Tables[0];
}

Innanzitutto, il tuo esempio di codice dovrebbe essere:

using (SqlConnection conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
using (SqlCommand cmd = new SqlCommand(reportDataSource, conn))
{
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
    cmd.Connection.Open();

    DataSet dset = new DataSet();
    new SqlDataAdapter(cmd).Fill(dset);
    this.gridDataSource.DataSource = dset.Tables[0];
}

Con il codice nella tua domanda, un'eccezione durante la creazione del comando comporterà che la connessione appena creata non verrà eliminata. Con quanto sopra, la connessione è correttamente disposta.

Se hai bisogno di gestire le eccezioni nella costruzione della connessione e del comando (così come quando le usi), sì, devi racchiudere il tutto in un tentativo / cattura:

try
{
    using (SqlConnection conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
    using (SqlCommand cmd = new SqlCommand(reportDataSource, conn))
    {
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
        cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
        cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
        cmd.Connection.Open();

        DataSet dset = new DataSet();
        new SqlDataAdapter(cmd).Fill(dset);
        this.gridDataSource.DataSource = dset.Tables[0];
    }
}
catch (RelevantException ex)
{
    // ...handling...
}

Ma non è necessario gestire la pulizia di conn o cmd ; è già stato fatto per te.

Contrasto con la stessa cosa senza usando :

SqlConnection conn = null;
SqlCommand cmd = null;
try
{
    conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString);
    cmd = new SqlCommand(reportDataSource, conn);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
    cmd.Connection.Open();

    DataSet dset = new DataSet();
    new SqlDataAdapter(cmd).Fill(dset);
    this.gridDataSource.DataSource = dset.Tables[0];
}
catch (RelevantException ex)
{
    // ...handling...
}
finally
{
    if (cmd != null)
    {
        try
        {
            cmd.Dispose();
        }
        catch { }
        cmd = null;
    }
    if (conn != null)
    {
        try
        {
            conn.Dispose();
        }
        catch { }
        conn = null;
    }
}
// And note that `cmd` and `conn` are still in scope here, even though they're useless

So quale preferirei scrivere. : -)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top