1

我有以下代码来从 db 检索数据,当我从 Visual Studio 运行代码分析时,它建议我调用 dispose 方法 on和SqlConnectionobjects 。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();
        }

但是,Visual Studio 像这样警告我——

CA2202:Microsoft.Usage:对象“sqlconn”可以在方法“test_search.Unnamed1_Click(object, EventArgs)”中多次处理。为避免生成 System.ObjectDisposedException,您不应在对象上多次调用 Dispose。:行:41

所以我想知道什么时候应该正确调用 dispose 方法。

4

8 回答 8

6

与其直接调用,不如dispose使用usingstatement,当代码执行移出其关联块时,它将确保为您执行此操作。例如:

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,使用block 会在调用完成后自动dispose 对象。

于 2012-10-29T11:16:58.090 回答
1

嗨,最好在最后编写 dispose 和 close 语句,因为如果代码中发生异常,它将执行。

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

当您关闭或处置连接时,它不会同时关闭连接。只有在刷新 appdomain 时才会关闭或清除连接池

于 2012-10-29T11:45:31.827 回答
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);
    }
}

This previous SO post有这个问题的最佳答案。

于 2012-10-29T11:23:27.307 回答
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

sqlconn.Dispose()在 finally 块中只是多余的。实际上,Close() 调用了 Dispose(),这就是对 Dispose 的特定调用引发错误的原因。

Close 和 Dispose 之间存在细微差别。

  • 多次调用 close 不会抛出异常。
  • 多次调用 dispose 将引发异常。

在您的情况下,很明显关闭又调用了 Dispose。因此,您对 Dispose 的第二次调用导致了异常。

始终使用USING作为更好的方法。

于 2012-10-29T11:15:26.180 回答
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 回答