w bloku „using” jest SqlConnection zamykane po powrocie lub wyjątku?


136

Pierwsze pytanie:
powiedz, że tak

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

Czy połączenie zostaje zamknięte? Ponieważ technicznie nigdy nie dotarliśmy do końca, }tak jak returnprzedtem.

Drugie pytanie:
tym razem mam:

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

Teraz powiedzmy, że gdzieś w trypliku pojawia się błąd i zostaje on przechwycony. Czy połączenie nadal jest zamykane? Ponownie, pomijamy resztę kodu w instrukcji tryi przechodzimy bezpośrednio do catchinstrukcji.

Czy myślę zbyt liniowo, jak to usingdziała? ie Czy Dispose()po prostu zostaje wywołany, gdy opuszczamy usingzakres?

Odpowiedzi:


178
  1. tak
  2. Tak.

Tak czy inaczej, gdy blok using zostanie zakończony (przez pomyślne zakończenie lub przez błąd), jest zamykany.

Chociaż myślę, że lepiej byłoby to zorganizować w ten sposób, ponieważ znacznie łatwiej jest zobaczyć, co się stanie, nawet dla nowego programisty utrzymania ruchu, który wesprze to później:

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

3
@TrueWill - zgadzam się. Po prostu trochę przesunąłem kod ze względu na strukturę.
— David,

10
Pytanie: Czy muszę nawet OTWIERAĆ połączenie, używając instrukcji Using?
— Fandango68

3
Również jeśli korzystasz z transakcji, mając try catchwewnątrz usingmożesz jawnie .Commitlub .Rollbacktransakcje w catch. Jest to zarówno bardziej czytelne, jak i wyraźne, i pozwala na zatwierdzenie, jeśli ma to sens, biorąc pod uwagę typ wyjątku. (Transakcje niejawnie wycofują się, conn.Closejeśli nie zostały zatwierdzone).
— Chris

8
@ Fernando68 Tak, nadal masz Openpołączenie. usinggwarantuje tylko, że Disposezostanie wywołana metoda obiektu .
— juharr

Mam zwrot ExecuteScalar wewnątrz przy użyciu bloków. A kiedy uruchamiam tę metodę po raz drugi, jest to bardzo szybkie, jakby połączenie było otwarte. Dlaczego za drugim razem jest tak szybko?
— pozytywna perspektywa

46

Tak na oba pytania. Instrukcja using zostaje skompilowana do bloku try / final

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

jest taki sam jak

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

Edycja: Naprawianie rzutowania na jednorazowe http://msdn.microsoft.com/en-us/library/yh598w02.aspx


to nie jest dokładnie to, ale jest wystarczająco blisko. dokładna różnica nie jest ważna.
— Bryan,

@Bryan tego nie rozumiał, czy możesz podać dokładną różnicę, może pomóc nam bardziej się oprzeć :-)
— mohits00691

Wow, to był komentarz zrobiony dawno temu :) Wygląda na to, że dzień po tym komentarzu była zmiana. Myślę, że to jest różnica, o której myślałem.
— Bryan,

@Bryan Tak, naprawiłem dokonaną korektę po Twoim komentarzu.
— Ryan Pedersen

17

Oto mój szablon. Wszystko, czego potrzebujesz, aby wybrać dane z serwera SQL. Połączenie jest zamykane i usuwane, a błędy w połączeniu i wykonywaniu są wychwytywane.

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

* Poprawiono: 2015-11-09 *
Zgodnie z sugestią NickG; Jeśli denerwuje Cię zbyt wiele aparatów ortodontycznych, sformatuj w ten sposób ...

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

Z drugiej strony, jeśli pracujesz dla gier EA lub DayBreak, możesz po prostu zrezygnować z przerw między wierszami, ponieważ są one przeznaczone tylko dla osób, które muszą później wrócić i spojrzeć na Twój kod, a kogo to naprawdę obchodzi? Czy mam rację? To znaczy 1 linia zamiast 23 oznacza, że ​​jestem lepszym programistą, prawda?

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

Uff ... OK. Wyciągnąłem to z mojego systemu i przez chwilę się bawię. Kontynuować.


6
Czy wiesz, że możesz nakładać instrukcje bez dodatkowych nawiasów klamrowych? Usuń ostatni nawias, a następnie umieść instrukcje using obok siebie :)
— NickG,

Tak jest. Dziękuję Ci. Jestem świadomy, ale chciałem, aby mój kod dokładnie pokazywał, co się dzieje, bez używania zbyt wielu innych skrótów. Dobra uwaga do dodania do czytelników końcowych.
— ShaneLS

Dlaczego używasz conn.Close();na końcu? Czy usingoświadczenie nie robi tego za Ciebie poprzez utylizację?
— Fredrick Gauss

Myślę, że teraz (od .net 3.5). Było to dla mnie niejasne na początku .net 2.0, więc po prostu przyzwyczaiłem się do sprawdzania i zamykania.
— ShaneLS

1
„średnia 1 linia zamiast 23 oznacza, że ​​jestem lepszym programistą, prawda?” Lubię cię :-D
— Philipp Müller

5

Dispose zostaje po prostu wywołane, gdy opuścisz zakres using. Celem „używania” jest zapewnienie programistom zagwarantowanego sposobu na pozbycie się zasobów.

Z MSDN :

Instrukcja using może zostać zakończona, gdy zostanie osiągnięty koniec instrukcji using lub jeśli zostanie zgłoszony wyjątek, a sterowanie pozostawi blok instrukcji przed końcem instrukcji.


5

Usinggeneruje próbę / w końcu wokół przydzielanego obiektu i woła Dispose()do Ciebie.

Oszczędza to kłopotów związanych z ręcznym tworzeniem bloku try / last i dzwonieniem Dispose()


3

W pierwszym przykładzie kompilator C # faktycznie przetłumaczy instrukcję using na następującą:

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

Instrukcje Final zawsze będą wywoływane przed powrotem funkcji, więc połączenie będzie zawsze zamykane / usuwane.

Tak więc w drugim przykładzie kod zostanie skompilowany w następujący sposób:

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

Wyjątek zostanie przechwycony w instrukcji last i połączenie zostanie zamknięte. Wyjątek nie będzie widoczny w klauzuli zewnętrznej catch.


1
bardzo dobre przykłady człowieku, ale muszę się nie zgodzić z twoim ostatnim komentarzem, jeśli wyjątek wystąpi w bloku using, zostanie złapany bez problemów na dowolnym zewnętrznym zaczepie, w rzeczywistości przetestowałem to, pisząc 2 używając bloków w bloku try / catch i ku mojemu zdziwieniu otrzymałem komunikat o błędzie wyjątku, który pochodzi z wewnętrznej sekundy przy użyciu bloku.
— WhySoSerious,

1

Napisałem dwie instrukcje using wewnątrz bloku try / catch i mogłem zobaczyć, że wyjątek jest przechwytywany w ten sam sposób, jeśli jest umieszczony w wewnętrznej instrukcji using , tak jak w przykładzie ShaneLS .

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

Bez względu na to, gdzie jest umieszczony tryb try / catch , wyjątek zostanie przechwycony bez problemów.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.