Wird in einem "using" -Block eine SqlConnection bei Rückgabe oder Ausnahme geschlossen?

136

Erste Frage:
Sag ich habe

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

Wird die Verbindung geschlossen? Denn technisch kommen wir nie so zum letzten }wie returnvorher.

Zweite Frage:
Diesmal habe ich:

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*/ }

Sagen trywir jetzt irgendwo in der wir bekommen einen Fehler und es wird gefangen. Wird die Verbindung immer noch geschlossen? Denn wieder überspringen wir den Rest des Codes in der tryund gehen direkt zur catchAnweisung.

Denke ich zu linear darüber, wie es usingfunktioniert? dh Wird Dispose()einfach angerufen, wenn wir den usingBereich verlassen ?

Marcus
quelle

Antworten:

178
  1. Ja
  2. Ja.

In beiden Fällen wird der using-Block geschlossen, wenn er beendet wird (entweder durch erfolgreichen Abschluss oder durch Fehler).

Obwohl ich denke, dass es besser wäre, sich so zu organisieren, weil es viel einfacher ist zu sehen, was passieren wird, selbst für den neuen Wartungsprogrammierer, der es später unterstützen wird:

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*/ 
    }
}
David
quelle
3
@ TrueWill - ich stimme zu. Ich habe den Code für die Struktur nur ein wenig verschoben.
David
10
Frage: Muss ich überhaupt eine Verbindung öffnen, wenn ich die Using-Anweisung verwende?
Fandango68
3
Auch wenn Sie Transaktionen verwenden, können Sie durch das try catchinnerhalb der usingSie explizit .Commitoder .RollbackTransaktionen in der catch. Dies ist sowohl lesbarer als auch expliziter und ermöglicht Ihnen das Festschreiben, wenn dies angesichts des Ausnahmetyps sinnvoll ist. (Transaktionen werden implizit zurückgesetzt, conn.Closewenn sie nicht festgeschrieben wurden.)
Chris
8
@ Fernando68 Ja, du musst noch Opendie Verbindung herstellen. usinggarantiert nur, dass die DisposeMethode des Objekts aufgerufen wird.
Juharr
Ich habe die Rückgabe ExecuteScalar mit Blöcken. Und wenn ich die Methode zum zweiten Mal ausführe, geht es sehr schnell, als ob die Verbindung offen wäre. Warum ist es beim zweiten Mal so schnell?
positive Perspektive
46

Ja zu beiden Fragen. Die using-Anweisung wird zu einem try / finally-Block kompiliert

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

ist das gleiche wie

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

Bearbeiten: Korrektur der Besetzung auf Einweg http://msdn.microsoft.com/en-us/library/yh598w02.aspx

Ryan Pedersen
quelle
es ist nicht genau das, aber es ist nah genug. Der genaue Unterschied ist nicht wichtig.
Bryan
@ Bryan hat es nicht verstanden, können Sie bitte den genauen Unterschied erwähnen, kann uns helfen, mehr zu lehnen :-)
mohits00691
Wow, das war ein Kommentar, der vor langer Zeit gemacht wurde :) Es sieht so aus, als ob es am Tag nach diesem Kommentar eine Bearbeitung gab. Ich denke, das ist der Unterschied, an den ich gedacht habe.
Bryan
@Bryan Ja, ich habe die Anpassung nach deinem Kommentar korrigiert.
Ryan Pedersen
17

Hier ist meine Vorlage. Alles, was Sie zum Auswählen von Daten von einem SQL Server benötigen. Die Verbindung wird geschlossen und entsorgt, und Fehler bei der Verbindung und Ausführung werden abgefangen.

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

* Überarbeitet: 09.11.2015 *
Wie von NickG vorgeschlagen; Wenn Sie zu viele Zahnspangen stören, formatieren Sie wie folgt ...

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

Wenn Sie für EA- oder DayBreak-Spiele arbeiten, können Sie auch auf Zeilenumbrüche verzichten, da diese nur für Leute gedacht sind, die später zurückkommen und sich Ihren Code ansehen müssen und wen interessiert das wirklich? Habe ich recht? Ich meine 1 Zeile statt 23 bedeutet, dass ich ein besserer Programmierer bin, oder?

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

Puh ... OK. Ich habe das aus meinem System herausgeholt und bin damit fertig, mich für eine Weile zu amüsieren. Mach weiter.

ShaneLS
quelle
6
Wussten Sie, dass Sie Anweisungen ohne zusätzliche Klammern stapeln können? Löschen Sie die letzte Klammer und platzieren Sie die using-Anweisungen nebeneinander :)
NickG
Jawohl. Danke dir. Ich bin mir bewusst, wollte aber, dass mein Code genau zeigt, was passiert ist, ohne zu viele andere Verknüpfungen zu verwenden. Guter Hinweis für die Endleser.
ShaneLS
Warum benutzt du conn.Close();am Ende? Tut die usingAussage das nicht für Sie durch Entsorgung?
Fredrick Gauss
Ich glaube das tut es jetzt (seit .net 3.5). Es war mir schon früh mit .net 2.0 unklar, also habe ich es mir zur Gewohnheit gemacht, es zu überprüfen und zu schließen.
ShaneLS
1
"Mittelwert 1 Zeile statt 23 bedeutet, dass ich ein besserer Programmierer bin, oder?" Ich mag dich :-D
Philipp Müller
5

Dispose wird einfach aufgerufen, wenn Sie den Anwendungsbereich verlassen. Die Absicht der "Verwendung" besteht darin, Entwicklern eine garantierte Möglichkeit zu bieten, um sicherzustellen, dass Ressourcen entsorgt werden.

Von MSDN :

Eine using-Anweisung kann entweder beendet werden, wenn das Ende der using-Anweisung erreicht ist oder wenn eine Ausnahme ausgelöst wird und das Steuerelement den Anweisungsblock vor dem Ende der Anweisung verlässt.

überstanden
quelle
5

Usinggeneriert einen try / finally-Vorgang für das zugewiesene Objekt und ruft Dispose()nach Ihnen.

Dies erspart Ihnen den Aufwand, den try / finally-Block manuell zu erstellen und aufzurufen Dispose()

Voodoo Kind
quelle
3

In Ihrem ersten Beispiel übersetzt der C # -Compiler die using-Anweisung tatsächlich in Folgendes:

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

Schließlich werden Anweisungen immer aufgerufen, bevor eine Funktion zurückkehrt, und die Verbindung wird immer geschlossen / entsorgt.

In Ihrem zweiten Beispiel wird der Code also wie folgt kompiliert:

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)
{
}

Die Ausnahme wird in der finally-Anweisung abgefangen und die Verbindung geschlossen. Die Ausnahme wird von der äußeren catch-Klausel nicht gesehen.

Kerri Brown
quelle
1
Sehr gute Beispiele, Mann, aber ich muss Ihrem letzten Kommentar nicht zustimmen. Wenn eine Ausnahme innerhalb eines using-Blocks auftritt, wird sie ohne Probleme bei einem äußeren Fang abgefangen. Tatsächlich habe ich sie getestet, indem ich 2 using-Blöcke in einen try / catch-Block geschrieben habe Zu meiner Überraschung wurde meine Ausnahmefehlermeldung angezeigt, die mit Block aus der inneren Sekunde kam.
WhySoSerious
1

Ich habe zwei using- Anweisungen in einen try / catch- Block geschrieben und konnte sehen, dass die Ausnahme auf die gleiche Weise abgefangen wurde, wenn sie genau wie das ShaneLS- Beispiel in die innere using- Anweisung eingefügt wurde .

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

Unabhängig davon, wo der Versuch / Fang platziert ist, wird die Ausnahme ohne Probleme abgefangen.

Warum so ernst
quelle