【问题标题】:Is SqlCommand.Dispose() required if associated SqlConnection will be disposed?如果将处置关联的 SqlConnection,是否需要 SqlCommand.Dispose()?
【发布时间】:2010-12-20 22:49:51
【问题描述】:

我通常使用这样的代码:

using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["MyConn"].ConnectionString))
{
   var command = connection.CreateCommand();
   command.CommandText = "...";
   connection.Open();
   command.ExecuteNonQuery();
}

我的command 会自动处置吗?或者不是,我必须将它包装到 using 块中?是否需要处置SqlCommand

【问题讨论】:

    标签: c# ado.net dispose sqlconnection sqlcommand


    【解决方案1】:

    这样做:

    using(var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["MyConn"].ConnectionString))
    using(var command = connection.CreateCommand())
    {
       command.CommandText = "...";
       connection.Open();
       command.ExecuteNonQuery();
    }
    

    不对命令调用 dispose 不会做任何坏事。但是,在其上调用 Dispose 会suppress the call to the finalizer,使调用 dispose 提高性能。

    【讨论】:

    • "不在命令上调用 dispose 不会做任何坏事。"没错,但不要习惯;这仅适用于 SqlCommand s。另一方面,例如,不释放SqlCeCommand导致您的移动设备很快耗尽内存。 (刚刚去过那里,做到了...)
    • Dispose 不会抑制终结,因为构造函数会这样做。
    • @EdwardBrey 哪个构造函数?
    • @EdwardBrey 仅在私有构造函数 SqlConnection(SqlConnection connection) 中完成,只能从 Clone() 方法调用。但是两个普通的公共构造函数都没有调用GC.SuppressFinalize,只有Close() 方法调用。所以我猜你错了。
    • @Sergey 对不起。我链接到错误的构造函数。重要的是SQLCommand constructor,它总是调用GC.SuppressFinalize
    【解决方案2】:

    最安全的策略是始终在对象上调用Dispose(),如果它实现了IDisposable,无论是显式地还是通过使用块。在某些情况下,它不是必需的,但无论如何调用它都不会导致问题(如果类编写正确)。此外,您永远不知道什么时候实现可能会改变,这意味着以前不需要调用的地方现在肯定需要调用。

    在您给出的示例中,您可以为命令添加一个额外的内部 using 块,以及为连接维护外部 using 块。

    【讨论】:

    • 是的,最安全的策略是始终丢弃一次性物品 - 特别是如果您创建了它!例如,编写一个接受流的方法并在该方法中处理流不是一个好主意。另一个需要注意的是,您不能总是通过 using 语句进行处置而不受惩罚。 WCF 代理是我所知道的唯一实际示例。如果远程端出现问题并且您遇到异常,则通道关闭并且 Dispose 然后抛出一个新异常,替换原来的异常,这可能是一个严重的问题。
    【解决方案3】:

    是的,你应该,即使它的实现目前没有做太多,你也不知道它将来会如何改变(例如更新的框架版本)。一般来说,您应该处置所有实现IDisposable 的对象以确保安全。

    但是,如果操作被延迟并且您无法控制整个范围(例如在异步工作时,或者在返回 SqlDataReader 左右时),您可以将 CommandBehavior 设置为 CloseConnection 以便一旦阅读器完成,连接就会为您正确关闭/处置。

    【讨论】:

      【解决方案4】:

      实际上,您可以跳过Dispose。它不会释放任何资源。自SQLCommand constructor does that 以来,它甚至不会抑制最终确定。

      理论上,Microsoft 可以更改实现以保存非托管资源,但我希望他们早在他们这样做之前就推出一个摆脱 Component 基类的 API。

      【讨论】:

        【解决方案5】:

        您可以使用 Reflector 或 dotPeek 或 https://referencesource.microsoft.com/ 找到这类东西。

        我有一个小挖掘(我建议你自己挖掘一下,尽管我没有那么努力,但要完全确定其余部分),看起来当你终止连接时,没有处理与该连接相关的任何孩子。此外,它实际上看起来不像一个命令的处置实际上做了那么多。它将一个字段设置为 null,将自己与容器分离(这可能会防止托管内存泄漏)并引发一个事件(这可能很重要,但我看不出谁在听这个事件)。

        无论哪种方式,最好在 using 块中使用这些东西,或者确保使用持有连接的对象中的处置模式处置它(如果您打算暂时持有该命令)。

        【讨论】:

          【解决方案6】:

          在我看来,为SqlConnectionSqlCommand 调用Dispose 是一种很好的做法,请使用以下代码

          using(var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["MyConn"].ConnectionString))
          try{
              using(var command = connection.CreateCommand())
              {
                 command.CommandText = "...";
                 connection.Open();
                 command.ExecuteNonQuery();
              }
          }
          catch(Exception ex){ //Log exception according to your own way
              throw;
          }
          finally{
              command.Dispose();
              connection.Close();
              connection.Dispose();
          }
          

          【讨论】:

          • 如果您有using,则不需要任何此代码:编译器将为您生成try/catch/dispose()。
          • @abatishchev,是的,你是对的,但捕获任何异常并正确记录它始终是一个好习惯。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-03-31
          • 1970-01-01
          • 2010-10-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多