в блоці "використання" закривається повернення 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.