1

dbからデータを取得するための以下のコードがあり、Visual Studioからコード分析を実行すると、SqlConnectionオブジェクトでdisposeメソッドを呼び出すように提案されます。DataTableSqlDataAdapter

        SqlConnection sqlconn = new SqlConnection(ConfigurationManager.ConnectionStrings["connstr"].ConnectionString);
        SqlCommand cmd = sqlconn.CreateCommand();
        cmd.CommandText = "SELECT * FROM tbl_serial WHERE serial = @serial";
        cmd.Parameters.AddWithValue("@serial", txtQuery.Text);
        DataTable dt = new DataTable();
        SqlDataAdapter da = new SqlDataAdapter();
        try
        {
            sqlconn.Open();
            da.SelectCommand = cmd;
            da.Fill(dt);

        }
        catch (SqlException ex)
        {
            lblStatus.Text = ex.Message;
        }
        finally
        {
            sqlconn.Close();
        }

        if (dt.Rows.Count > 0)
        {
            lblStatus.Text = "FOUND!";
        }
        else
        {
            lblStatus.Text = "NOT FOUND!";
        }

そして、これは私がこれを行うのは初めてです、私はsqlconnちょうどこのようにdisposeメソッドを呼び出しました-

        finally
        {
            sqlconn.Close();
            sqlconn.Dispose();
        }

しかし、その後、VisualStudioはこのように私に警告します-

CA2202:Microsoft.Usage:オブジェクト'sqlconn'は、メソッド'test_search.Unnamed1_Click(object、EventArgs)'で複数回破棄できます。System.ObjectDisposedExceptionの生成を回避するには、オブジェクトに対してDisposeを複数回呼び出さないでください。:行:41

したがって、disposeメソッドを正しく呼び出す必要があるのはいつかを知りたいのです。

4

8 に答える 8

6

dispose直接呼び出すのではなく、usingステートメントを使用します。これにより、コードの実行が関連するブロックから移動したときに確実に実行されます。例えば:

SqlConnection con;

using (con = new SqlConnection(/* ... */)) {
    // Do your stuff with `con`
}

これは、一般的に使い捨てで使用するパターンです。

于 2012-10-29T11:15:37.403 に答える
3

Disposeこれが、すべて/Close手動で行うことなく書き直されたコードです。

using (var sqlconn = new SqlConnection(ConfigurationManager.ConnectionStrings["connstr"].ConnectionString))
using (var cmd = sqlconn.CreateCommand())
{
    cmd.CommandText = "SELECT * FROM tbl_serial WHERE serial = @serial";
    cmd.Parameters.AddWithValue("@serial", txtQuery.Text);
    using (var dt = new DataTable())
    using (var da = new SqlDataAdapter())
    {
        try
        {
            sqlconn.Open();
            da.SelectCommand = cmd;
            da.Fill(dt);

        }
        catch (SqlException ex)
        {
            lblStatus.Text = ex.Message;
        }

        lblStatus.Text = dt.Rows.Count > 0 ? "FOUND!" : "NOT FOUND!";
    } 
}
于 2012-10-29T11:20:27.117 に答える
2

接続の破棄を処理する最良の方法は、次を使用することです。

したがって、コードは次のようになります。

  using(SqlConnection sqlconn = new SqlConnection(ConfigurationManager.ConnectionStrings["connstr"].ConnectionString))
  { 
      code here..
  }

SqlconnectionはIDisposableを実装しているため、ブロックを使用すると、呼び出しの完了後にオブジェクトが自動的に破棄されます。

于 2012-10-29T11:16:58.090 に答える
1

sqlconn.Dispose()そのfinallyブロックでは冗長です。実際にはClose()がDispose()を呼び出しているため、Disposeへの特定の呼び出しでエラーがスローされます。

CloseとDisposeには微妙な違いがあります。

  • closeを複数回呼び出しても、例外はスローされません。
  • disposeを複数回呼び出すと、例外がスローされます。

あなたの場合、クローズがDisposeを呼び出したことは非常に明白です。したがって、Disposeへの2回目の呼び出しは、例外を引き起こしています。

より良いアプローチとして、常にUSINGを使用してください。

于 2012-10-29T11:15:26.180 に答える
1
using(SqlConnection sqlconn = new SqlConnection(ConnectionStrings))
{ 
   //Automatically this block will disposes the SqlConnection
}

or 

you can dispose all your objects in Finally Block
于 2012-10-29T11:18:35.777 に答える
1

私は他の人たちに同意しusingます。これを使用するのは間違いなくベストプラクティスです。ただし、finallyブロックを次のように変更する必要があります。

finally
{
    if (sqlconn != null)
    {
        if (sqlConn.ConnectionState == ConnectionState.Open) sqlconn.Close();
        sqlConn.Dispose();
        GC.SuppressFinalize(sqlConn);
        sqlConn = null;
    }
}

ガベージコレクターには、すでに廃棄しているので、GC.SuppressFinalizeわざわざ廃棄しないように指示しsqlConnています。これを行うことは可能ですが、ベストプラクティスはIDisposable、オブジェクトに実装し、コントラクトのメソッドですべてのクリーンアップを処理することであると常に信じています。Disposeこれもオブジェクトを呼び出しGC.SuppressFinalize(this)ます。

public class MyObject : IDisposable
{
    private SqlConnection _sqlConn;

    public MyObject()
    {
        _sqlConn = new SqlConnection("connection string");
        _sqlConn.Open();
    }

    void IDisposable.Dispose()
    {
        if (_sqlConn != null)
        {
            if (_sqlConn.ConnectionState == ConnectionState.Open)
            {
                _sqlConn.Close();
            }

            _sqlConn.Dispose();
            _sqlConn = null;
        }

        // tell the garbage collector to ignore this object as you've tidied it up yourself
        GC.SuppressFinalize(this);
    }
}

この以前のSO投稿には、この質問に対する最良の回答があります。

于 2012-10-29T11:23:27.307 に答える
1

こんにちは、コードで例外が発生した場合に実行されるため、最後にdisposeとcloseステートメントを記述することをお勧めします。

finally
{
   sqlconn.Close();
   SqlConnection.ClearAllPool();  // this statement clears pool
   sqlConn.Dispose(); 
}

接続を閉じるか破棄しても、同時に接続は閉じられません。appdomainが更新された場合にのみ、接続プールを閉じるかクリアします

于 2012-10-29T11:45:31.827 に答える
0

コードを次のようにフォーマットすることをお勧めします。

using (var sqlconn = new SqlConnection(...)){
  try {
            sqlconn.Open();
            da.SelectCommand = cmd;
            da.Fill(dt);
  }
  catch (SqlException ex) {
            lblStatus.Text = ex.Message;
  }
}

    if (dt.Rows.Count > 0)
    {
        lblStatus.Text = "FOUND!";
    }
    else
    {
        lblStatus.Text = "NOT FOUND!";
    }

このようにして、using構文に接続の破棄/クローズを処理させ、コードは重要なロジックに焦点を合わせます。

于 2012-10-29T11:19:26.290 に答える