web-dev-qa-db-ja.com

「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のように、最後の}に到達することはありません。

2番目の質問:
今回は:

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がどのように機能するかについて、あまりにも直線的に考えていますか?すなわち、usingスコープを出るときにDispose()が単に呼び出されますか?

130
Marcus
  1. はい
  2. はい。

いずれにせよ、usingブロックが(正常終了またはエラーにより)終了すると、閉じられます。

私はそれがbetterになると思いますが、何が起こるかを見るのがずっと簡単だから後でサポートする新しいメンテナンスプログラマ向け:

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

両方の質問に答えます。 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

44
Ryan Pedersen

これが私のテンプレートです。 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(); }

うん... OK私はそれを自分のシステムから取り出し、しばらく自分自身を面白くしました。続ける。

14
ShaneLS

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

MSDN から:

Usingステートメントは、usingステートメントの終わりに到達したとき、または例外がスローされ、ステートメントの終わりの前に制御がステートメントブロックから離れたときに終了できます。

5
overstood

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

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

4
VoodooChild

最初の例では、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節には表示されません。

2
Kerri Brown

try/catchブロック内に2つのusingステートメントを書いたところ、例外が発生していることがわかりました。 ShaneLS example と同じように、内部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がどこに配置されていても、例外は問題なくキャッチされます。

1
WhySoSerious