В блоке "using" есть SqlConnection, закрытое по возврату или исключению?

Первый вопрос:
Скажем, у меня

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?

Ответ 1

  • Да
  • Да.

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

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

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

}

Ответ 2

Да на оба вопроса. Оператор 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

Ответ 3

Вот мой шаблон. Все, что вам нужно для выбора данных с 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(); }

Фу... Хорошо. Я получил это из своей системы и немного занят собой. Продолжайте.

Ответ 4

Dispose просто вызывается, когда вы покидаете область использования. Цель "использования" - предоставить разработчикам гарантированный способ убедиться, что ресурсы удалены.

От MSDN:

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

Ответ 5

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

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

Ответ 6

В вашем первом примере компилятор С# фактически преобразует оператор 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)
{
}

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

Ответ 7

Я написал два оператора 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);
     }

Независимо от места размещения try/catch исключение будет устранено без проблем.