「using」ブロックで、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
@TrueWill-同意します。私はコードを構造のために少し動かしただけです。
デビッド

10
質問:Usingステートメントを使用する場合、接続を開く必要さえありますか?
Fandango68

3
また、トランザクションを使用try catchしているusing場合は、内にを指定することにより、明示的に、.Commitまたはで.Rollbackトランザクションを実行できますcatch。これは、より読みやすく明示的であり、例外のタイプを考慮して、それが理にかなっている場合はコミットできます。(conn.Closeコミットされない場合、トランザクションは暗黙的にロールバックされます。)
Chris

8
@ Fernando68はい、まだOpen接続する必要があります。 usingオブジェクトのDisposeメソッドが呼び出されることを保証するだけです。
juharr 2015

ブロックを使用して内部にExecuteScalarを返します。また、メソッドを2回目に実行すると、接続が開いているように非常に高速です。なぜ二度目はそんなに速いのですか?
前向きな見方

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


それは正確ではありませんが、十分に近いです。正確な違いは重要ではありません。
ブライアン

@ブライアンはそれを理解できませんでした、正確な違いを教えてください、私たちがもっと傾けるのを助けることができます:-)
mohits00691

うわー、それはずっと前に行われたコメントでした:)私はそのコメントをした翌日に編集があったように見えます。それが私が考えていた違いだと思います。
ブライアン

@ブライアンはい、私はあなたのコメントの後に修正を行いました。
ライアンペダーセン

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のゲームで作業している場合は、改行を忘れることもできます。改行は、後で戻ってコードを確認する必要があり、本当に気にかけている人のためのものだからです。私は正しいですか?23ではなく1行ということは、私がより優れたプログラマであることを意味しますよね?

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

ふew… 私はそれを自分のシステムから取り出して、しばらく面白がっています。続ける。


6
追加のブレースなしでステートメントを使用してスタックできることをご存知ですか?最後のブレースを削除してから、usingステートメントを隣同士に配置します:)
NickG

かしこまりました。ありがとうございました。私は認識していますが、他の多くのショートカットを使用せずに、コードに何が起こっているのかを正確に示すことを望んでいました。最後の読者に付け加えてください。
ShaneLS 2015年

conn.Close();最後になぜ使うの?using陳述はあなたのために処分することによってそれをしませんか?
Fredrick Gauss

今はそうだと思います(.net 3.5以降)。.net 2.0の初期の段階では不明瞭だったので、チェックして閉じるのを習慣にしました。
ShaneLS 2016

1
「23行ではなく1行ということは、私がより優れたプログラマであることを意味しますよね?」あなたのようなI - D
フィリップ・ミュラー

5

Disposeは、使用範囲を離れると呼び出されます。「使用」の目的は、リソースを確実に破棄するための保証された方法を開発者に提供することです。

MSDNから:

usingステートメントは、usingステートメントの終わりに達したとき、または例外がスローされ、コントロールがステートメントの終わりの前にステートメントブロックを離れたときに終了できます。


5

Using割り当てられているオブジェクトの周りにtry / finallyを生成し、呼び出しDispose()ます。

手動でtry / finallyブロックを作成して呼び出す手間を省きます Dispose()


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

最後に、ステートメントは常に関数が戻る前に呼び出されるため、接続は常にクローズ/破棄されます。

したがって、2番目の例では、コードは次のようにコンパイルされます。

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ブロック内で例外が発生した場合、外側のキャッチで問題なくキャッチされます。実際、try / catchブロック内に2つのusingブロックを記述してテストしました。 、そして驚いたことに、ブロックを使用して1秒以内に発生した例外エラーメッセージが表示されました。
WhySoSerious

1

私はtry / catchブロック内に2つのusingステートメント記述しましたが、それがShaneLSのと同じように内部のusingステートメント内に配置されている場合、例外が同じ方法でキャッチされていることがわかりました。

     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がどこにあっても、例外は問題なくキャッチされます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.