в блоке «использование» SqlConnection закрывается при возврате или исключении? - PullRequest
131 голосов
/ 17 января 2011

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

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?

Ответы [ 7 ]

171 голосов
/ 17 января 2011
  1. Да
  2. Да.

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

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

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*/ 
    }
}
44 голосов
/ 18 января 2011

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

14 голосов
/ 09 августа 2013

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

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

5 голосов
/ 18 января 2011

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

С MSDN :

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

4 голосов
/ 18 января 2011

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

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

2 голосов
/ 18 января 2011

В вашем первом примере компилятор 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 голос
/ 21 февраля 2014

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

...