Muss SqlDataReader manuell geschlossen und entsorgt werden?

90

Ich arbeite hier mit Legacy-Code und es gibt viele Instanzen SqlDataReader, die niemals geschlossen oder entsorgt werden. Die Verbindung ist geschlossen, aber ich bin nicht sicher, ob es notwendig ist, den Reader manuell zu verwalten.

Könnte dies zu einer Leistungsverlangsamung führen?

Jon Ownbey
quelle

Antworten:

124

Vermeiden Sie solche Leser:

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

Schließen Sie sie stattdessen mit Anweisungen ein:

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

Die using-Anweisung stellt die korrekte Entsorgung des Objekts und die Freisetzung von Ressourcen sicher.

Wenn Sie es vergessen, überlassen Sie die Reinigung dem Müllsammler, was eine Weile dauern kann.

Codebrain
quelle
24
In beiden Beispielen benötigen Sie die Anweisung .Close () nicht: Sie wird vom Aufruf .Dispose () verarbeitet.
Joel Coehoorn
7
Wahrscheinlich möchten Sie überprüfen, ob es .HasRows eher als null.
JonH
3
@ Andrew Wenn ExecuteReader eine Ausnahme auslöst, wie kann sie null zurückgeben?
csauve
7
@JohH: Die while-Datei (reader.Read ()) im Beispiel entspricht der von .HasRows, und Sie müssen trotzdem .Read ausführen, um den Leser in die erste Zeile zu bringen.
Csauve
1
@csauve Du hast recht, ich denke, es muss nicht null zurückgegeben haben. Ich bin mir nicht sicher, warum ich den Wert der SqlDataReader-Variablen betrachtet habe.
Andrew
53

Beachten Sie, dass das Entsorgen eines mit SqlCommand.ExecuteReader () instanziierten SqlDataReader die zugrunde liegende Verbindung nicht schließt / entsorgt.

Es gibt zwei gängige Muster. Im ersten Fall wird der Leser im Rahmen der Verbindung geöffnet und geschlossen:

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

Manchmal ist es praktisch, wenn eine Datenzugriffsmethode eine Verbindung öffnet und einen Leser zurückgibt. In diesem Fall ist es wichtig, dass der zurückgegebene Reader mit CommandBehavior.CloseConnection geöffnet wird, damit durch Schließen / Entsorgen des Readers die zugrunde liegende Verbindung geschlossen wird. Das Muster sieht ungefähr so ​​aus:

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

und der aufrufende Code muss den Leser nur so entsorgen:

using(SqlDataReader reader = ExecuteReader(...))
{
    ... do your stuff ...
} // reader and connection are closed here.
Joe
quelle
Im zweiten Codeausschnitt, in dem die Methode einen SqlDataReader zurückgibt, wird der Befehl nicht entsorgt. Ist das in Ordnung und ist es in Ordnung, den Befehl zu entsorgen (ihn in einen using-Block einzuschließen) und dann den Reader zurückzugeben?
lernen
@alwayslearning das ist genau das Szenario, das ich habe ...... können Sie den SqlCommand schließen / entsorgen, wenn Sie den SqlDataReader an den Aufrufer zurückgeben?
Ganders
1
Das ist schlecht. Wenn Sie es WIRKLICH nicht ertragen können, usings zu verwenden, rufen Sie dispose im finally {}Block nach catch auf. So wie dies geschrieben ist, würden erfolgreiche Befehle niemals geschlossen oder entsorgt werden.
smdrager
2
@smdrager, wenn Sie die Antwort genauer lesen, spricht er über eine Methode, die einen Leser zurückgibt. Wenn Sie .ExecuteReader (CommandBehavior.CloseConnection) verwenden; Durch Entsorgen des READER wird die Verbindung geschlossen. Die aufrufende Methode muss den resultierenden Reader also nur in eine using-Anweisung einschließen. using (var rdr = SqlHelper.GetReader ()) {// ...} Wenn Sie es im finally-Block schließen, kann Ihr Reader nicht lesen, da die Verbindung geschlossen ist.
Sinaesthetic
@ganders - zurück zu diesem alten Beitrag: Ja, Sie können und sollten wahrscheinlich den SqlCommand entsorgen - hat das Beispiel aktualisiert, um dies zu tun.
Joe
11

Um sicher zu gehen, schließen Sie jedes SqlDataReader-Objekt in eine using-Anweisung ein .

Kon
quelle
Meinetwegen. Macht es jedoch tatsächlich einen Unterschied in der Leistung, wenn es keine using-Anweisung gibt?
Jon Ownbey
Eine using-Anweisung entspricht dem Umschließen des DataReader-Codes in einen try..finally ... -Block mit der Methode close / dispose im Abschnitt finally. Grundsätzlich "garantiert" es nur, dass das Objekt ordnungsgemäß entsorgt wird.
Todd
Dies ergibt sich direkt aus dem von mir angegebenen Link: "Die using-Anweisung stellt sicher, dass Dispose aufgerufen wird, auch wenn beim Aufrufen von Methoden für das Objekt eine Ausnahme auftritt."
Kon
5
Fortsetzung ... "Sie können das gleiche Ergebnis erzielen, indem Sie das Objekt in einen try-Block einfügen und dann Dispose in einem finally-Block aufrufen. Auf diese Weise wird die using-Anweisung vom Compiler übersetzt."
Kon
5

Schließen Sie Ihren SQLDataReader einfach mit der Anweisung "using" ein. Das sollte sich um die meisten Ihrer Probleme kümmern.

JW
quelle