в блоці "використання" закривається повернення 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 (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 - я згоден. Я просто перемістив код трохи для структури.
— Девід

10
Питання: Чи потрібно навіть ВІДКРИТИ з'єднання під час використання оператора Use?
— Fandango68

3
Крім того, якщо ви використовуєте транзакції, маючи внутрішні try catchзаписи, які usingви можете явно, .Commitабо .Rollbackтранзакції в catch. Це є і більш зрозумілим та явним, і дозволяє вам здійснити, якщо це має сенс, враховуючи тип винятку. (Операції неявно повертаються назад, conn.Closeякщо вони не здійснені.)
— Кріс

8
@ Fernando68 Так, вам все одно доведеться Openпідключитися. usingгарантує лише те, що Disposeметод об'єкта викликається.
— juharr

У мене є повернення ExecuteScalar всередині за допомогою блоків. І коли я запускаю метод вдруге, це дуже швидко, наче з'єднання було відкритим. Чому це так швидко вдруге?
— позитивна перспектива

46

Так, для обох питань. Оператор, що використовує, збирається в блок "спробувати / остаточно"

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


це не зовсім так, але це досить близько. точна різниця не важлива.
— Брайан

@Bryan цього не зрозумів, будь ласка, будь ласка, зазначте точну різницю, чи можете ви допомогти нам нахилитися більше :-)
— mohits00691

Нічого собі, це був коментар, зроблений дуже давно :) Виглядає так, ніби було редагування на наступний день після того, як я зробив цей коментар. Я думаю, що це різниця, про яку я думав.
— Брайан

@Bryan Так, я виправив внесені корективи після вашого коментаря.
— Райан Педерсен

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

Фу ... добре. Я вийшов із своєї системи і на деякий час розважався. Продовжуй.


6
Чи знаєте ви, що можете складати з використанням операторів без додаткових дужок? Видаліть останню дужку, після чого поставте використовуючі висловлювання поруч один з одним :)
— NickG

Так, сер. Дякую. Я знаю, але хотів, щоб мій код показував саме те, що відбувається, не використовуючи занадто багато інших скорочень. Хороша примітка, проте, щоб додати до кінцевих читачів, хоча.
— ShaneLS

Для чого ви використовуєте conn.Close();в кінці? Чи не usingзаява робить це для вас через розпорядження?
— Фредрік Гаус

Я вважаю, що це зараз (з .net 3.5). Мені було незрозуміло на початку з .net 2.0, тому я просто зробив звичку перевіряти та закривати.
— ShaneLS

1
"означає 1 рядок замість 23 означає, що я кращий програміст, правда?" Мені подобається :-D
— Philipp Müller

5

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

Від MSDN :

Використовуючий оператор може бути закритий або тоді, коли досягнуто кінця використовуючого оператора, або якщо буде викинуто виняток і контроль залишає блок операторів до кінця оператора.


5

Usingстворює спробу / нарешті навколо виділеного об'єкта та закликає Dispose()вас.

Це заощаджує вам клопоту вручну створювати спробувати / остаточно заблокувати та виклик Dispose()


3

У вашому першому прикладі компілятор 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();
}

Нарешті, оператори завжди будуть викликатись, перш ніж функція повернеться, і тому з'єднання завжди буде закритим / розпорядженим.

Отже, у вашому другому прикладі код буде складений таким чином:

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

Виняток буде зафіксовано в остаточному операторі і з'єднання буде закрите. Виняток не буде розглядатися із положення про зовнішній вилов.


1
дуже хороші приклади людина, але я повинен не погодитися з вашим останнім коментарем, якщо виняток трапляється в рамках використання блоку, він буде без проблем потрапляти на будь-який зовнішній улов, адже я перевірив це, записавши 2, використовуючи блоки всередині блоку спробу / лову. , і на моє здивування, я отримав повідомлення про помилку виключення, яке надійшло з внутрішньої секунди за допомогою блоку.
— WhySoSerious

1

Я написав два, використовуючи оператори всередині блоку 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);
     }

Незалежно від того, де розміщена спроба / улов , виняток буде викрито без проблем.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.