È necessario chiudere e smaltire manualmente SqlDataReader?


90

Sto lavorando con il codice legacy qui e ci sono molte istanze SqlDataReaderche non vengono mai chiuse o eliminate. La connessione è chiusa ma non sono sicuro se sia necessario gestire manualmente il lettore.

Questo potrebbe causare un rallentamento delle prestazioni?

Risposte:


124

Cerca di evitare di usare lettori come questo:

SqlConnection connection = new SqlConnection("connection string");
SqlCommand cmd = new SqlCommand("SELECT * FROM SomeTable", connection);
SqlDataReader reader = cmd.ExecuteReader();
connection.Open();
if (reader != null)
{
      while (reader.Read())
      {
              //do something
      }
}
reader.Close(); // <- too easy to forget
reader.Dispose(); // <- too easy to forget
connection.Close(); // <- too easy to forget

Invece, avvolgili nelle istruzioni using:

using(SqlConnection connection = new SqlConnection("connection string"))
{

    connection.Open();

    using(SqlCommand cmd = new SqlCommand("SELECT * FROM SomeTable", connection))
    {
        using (SqlDataReader reader = cmd.ExecuteReader())
        {
            if (reader != null)
            {
                while (reader.Read())
                {
                    //do something
                }
            }
        } // reader closed and disposed up here

    } // command disposed here

} //connection closed and disposed here

La dichiarazione using garantirà il corretto smaltimento dell'oggetto e la liberazione delle risorse.

Se dimentichi, stai lasciando la pulizia al netturbino, che potrebbe richiedere del tempo.


24
Non hai bisogno dell'istruzione .Close () in nessuno dei due esempi: è gestita dalla chiamata .Dispose ().
Joel Coehoorn

7
Probabilmente vorrebbe controllare se ha .HasRows piuttosto che null.
JonH

3
@Andrew Se ExecuteReader genera un'eccezione, come può restituire null?
csauve

7
@JohH: il while (reader.Read ()) nell'esempio ha lo stesso risultato di .HasRows, e devi comunque leggere per far avanzare il lettore alla prima riga.
csauve

1
@csauve Hai ragione, immagino che non debba essere stato restituito null. Non sono sicuro del motivo per cui stavo guardando il valore della variabile SqlDataReader.
Andrew

53

Si noti che l'eliminazione di un SqlDataReader istanziato utilizzando SqlCommand.ExecuteReader () non chiuderà / eliminerà la connessione sottostante.

Esistono due modelli comuni. Nella prima, il lettore viene aperto e chiuso nell'ambito della connessione:

using(SqlConnection connection = ...)
{
    connection.Open();
    ...
    using(SqlCommand command = ...)
    {
        using(SqlDataReader reader = command.ExecuteReader())
        {
            ... do your stuff ...
        } // reader is closed/disposed here
    } // command is closed/disposed here
} // connection is closed/disposed here

A volte è conveniente che un metodo di accesso ai dati apra una connessione e restituisca un lettore. In questo caso è importante che il lettore restituito venga aperto utilizzando CommandBehavior.CloseConnection, in modo che la chiusura / eliminazione del lettore chiuderà la connessione sottostante. Il modello è simile a questo:

public SqlDataReader ExecuteReader(string commandText)
{
    SqlConnection connection = new SqlConnection(...);
    try
    {
        connection.Open();
        using(SqlCommand command = new SqlCommand(commandText, connection))
        {
            return command.ExecuteReader(CommandBehavior.CloseConnection);
        }
    }
    catch
    {
        // Close connection before rethrowing
        connection.Close();
        throw;
    }
}

e il codice chiamante deve solo disporre il lettore così:

using(SqlDataReader reader = ExecuteReader(...))
{
    ... do your stuff ...
} // reader and connection are closed here.

Nel secondo frammento di codice in cui il metodo restituisce un SqlDataReader il comando non viene eliminato. Va bene e va bene eliminare il comando (racchiuderlo in un blocco using) e poi restituire il lettore?
imparare sempre il

@alwayslearning è esattamente lo scenario che ho ...... puoi chiudere / eliminare SqlCommand quando restituisci SqlDataReader al chiamante?
ganders

1
Questo non va bene. Se DAVVERO non puoi sopportare di usare usings, chiama dispose nel finally {}blocco dopo la cattura. Nel modo in cui viene scritto, i comandi di successo non verrebbero mai chiusi o eliminati.
smdrager

2
@smdrager, se leggi la risposta più da vicino, sta parlando di un metodo che restituisce un lettore. Se utilizzi .ExecuteReader (CommandBehavior.CloseConnection); quindi eliminando il READER, la connessione verrà chiusa. Quindi il metodo chiamante deve solo racchiudere il lettore risultante in un'istruzione using. using (var rdr = SqlHelper.GetReader ()) {// ...} se lo chiudessi nel blocco finalmente, il tuo lettore non riuscirebbe a leggere perché la connessione è chiusa.
Sinaesthetic

@ganders - tornando a questo vecchio post: sì, puoi e probabilmente dovresti eliminare SqlCommand - ha aggiornato l'esempio per farlo.
Joe

11

Per sicurezza, racchiudi ogni oggetto SqlDataReader in un'istruzione using .


Giusto. Tuttavia, fa effettivamente la differenza in termini di prestazioni se non c'è una dichiarazione using?
Jon Ownbey

Un'istruzione using equivale a racchiudere il codice DataReader in un blocco try..finally ..., con il metodo close / dispose nella sezione finalmente. Fondamentalmente, "garantisce" solo che l'oggetto sarà smaltito correttamente.
Todd

Questo è direttamente dal collegamento che ho fornito: "L'istruzione using garantisce che Dispose venga chiamato anche se si verifica un'eccezione mentre si chiamano metodi sull'oggetto."
Kon

5
Continua ... "È possibile ottenere lo stesso risultato inserendo l'oggetto in un blocco try e quindi chiamando Dispose in un blocco finalmente; infatti, è così che l'istruzione using viene tradotta dal compilatore."
Kon

5

Metti semplicemente il tuo SQLDataReader nell'istruzione "using". Questo dovrebbe occuparsi della maggior parte dei tuoi 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.