È 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.