в «использующем» блоке SqlConnection закрывается при возврате или исключении?

136

Первый вопрос:
скажи, что у меня есть

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

Соединение закрывается? Потому что технически мы никогда не добираемся до последнего, }как returnраньше.

Второй вопрос: на
этот раз у меня есть:

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

Теперь, скажем где-нибудь в, tryмы получаем ошибку, и она ловится. Соединение все еще закрывается? Потому что, опять же, мы пропускаем оставшуюся часть кода tryи переходим непосредственно к catchвыражению.

Я слишком линейно думаю о том, как usingработает? т.е. Dispose()просто вызывается, когда мы покидаем usingсферу?

Маркус
источник

Ответы:

178
  1. да
  2. Да.

В любом случае, при выходе из блока using (либо при успешном завершении, либо по ошибке) он закрывается.

Хотя я думаю, что было бы лучше организовать так, потому что намного проще увидеть, что произойдет, даже для нового программиста по обслуживанию, который поддержит его позже:

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
@ Правда - согласен. Я просто немного передвинул код для структуры.
Дэвид
10
Вопрос: нужно ли даже открывать соединение при использовании оператора Using?
Fandango68
3
Также, если вы используете транзакции, имея try catchвнутри usingвы можете явно .Commitили .Rollbackтранзакции в catch. Это и более читабельно, и явно, и позволяет вам фиксировать, если это имеет смысл, учитывая тип исключения. (Транзакции неявно откатываются, conn.Closeесли не зафиксированы.).
Крис
8
@ Fernando68 Да, у тебя все еще есть Openсвязь. usingтолько гарантирует, что метод объекта Disposeвызывается.
15
У меня есть возврат ExecuteScalar внутри, используя блоки. И когда я запускаю метод во второй раз, он очень быстрый, как будто соединение было открыто. ПОЧЕМУ так быстро во второй раз?
позитивная перспектива
46

Да, на оба вопроса. Оператор using компилируется в блок try / finally

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

такой же как

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

Изменить: Исправление приведение к одноразовым http://msdn.microsoft.com/en-us/library/yh598w02.aspx

Райан Педерсен
источник
это не совсем так, но достаточно близко. Точная разница не важна.
Брайан
@ Брайан не понял, не могли бы вы назвать точную разницу, можете помочь нам больше
поститься
Ого, это был комментарий, сделанный давным-давно :) Похоже, что редактирование было сделано на следующий день после того, как я сделал этот комментарий. Я думаю, что это разница, о которой я думал.
Брайан
@ Брайан Да, я исправил внесенные коррективы после вашего комментария.
Райан Педерсен
17

Вот мой шаблон. Все, что вам нужно, чтобы выбрать данные с сервера SQL. Соединение закрыто и удалено, и ошибки в соединении и выполнении обнаружены.

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

* Пересмотрено: 2015-11-09 *
По предложению NickG; Если вас раздражает слишком много скобок, отформатируйте их так ...

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

Опять же, если вы работаете в играх EA или DayBreak, вы можете просто отказаться от любых разрывов строк, потому что они предназначены только для людей, которые должны вернуться и посмотреть на ваш код позже, и кого это волнует? Я прав? Я имею в виду 1 строку вместо 23 означает, что я лучший программист, верно?

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

Фу ... ОК. Я вытащил это из своей системы и немного позабавился. Продолжать.

ShaneLS
источник
6
Знаете ли вы, что вы можете сложить с помощью операторов без дополнительных скобок? Удалите последнюю фигурную скобку, затем поместите операторы использования рядом друг с другом :)
NickG
Да сэр. Спасибо. Я знаю, но хотел, чтобы мой код точно показывал то, что происходило, не используя слишком много других ярлыков. Хорошая заметка, чтобы добавить в конце читателей, хотя.
ShaneLS
Почему вы используете conn.Close();в конце? Разве usingзаявление не делает это для вас через утилизацию?
Фредрик Гаусс
Я верю, что это происходит сейчас (начиная с .net 3.5). С самого начала .net 2.0 мне было неясно, поэтому я просто сделал привычку проверять и закрывать.
ShaneLS
1
"означает, что 1 строка вместо 23 означает, что я лучший программист, верно?" Ты мне нравишься :-D
Филипп Мюллер
5

Dispose просто вызывается, когда вы выходите из области использования. Намерение «использовать» состоит в том, чтобы предоставить разработчикам гарантированный способ обеспечить утилизацию ресурсов.

Из MSDN :

Оператор using может быть завершен либо по достижении конца оператора using, либо в случае возникновения исключения, когда элемент управления покидает блок оператора до конца оператора.

overstood
источник
5

Usingгенерирует попытку / окончание вокруг объекта, который выделяется, и вызывает Dispose()вас.

Это избавляет вас от необходимости вручную создавать блок try / finally и вызывать Dispose()

VoodooChild
источник
3

В вашем первом примере компилятор C # фактически преобразует оператор using в следующее:

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

Наконец, операторы всегда будут вызываться до того, как функция вернется, и поэтому соединение всегда будет закрыто / удалено.

Итак, во втором примере код будет скомпилирован в следующее:

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

Исключение будет зафиксировано в операторе finally, и соединение будет закрыто. Исключение не будет видно во внешнем предложении catch.

Керри Браун
источник
1
Очень хороший пример, парень, но я должен не согласиться с вашим последним комментарием, если в блоке using возникает исключение, оно будет перехвачено без проблем при любом внешнем перехвате, на самом деле я протестировал его, написав 2 с использованием блоков внутри блока try / catch и, к моему удивлению, я получил сообщение об ошибке исключения, которое пришло из внутренней секунды с помощью блока.
WhySoSerious
1

Я написал два оператора using внутри блока try / catch, и я увидел, что исключение перехватывалось таким же образом, если оно помещалось во внутренний оператор using, как в примере с 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);
     }

Независимо от того, где размещена попытка / отлов , исключение будет поймано без проблем.

Почему ты такой серьезный
источник