如果已经关闭SqlConnection,是否需要关闭/处理SqlDataReader?

4
我注意到了这个问题,但我的问题更加具体。
使用关闭DataReader的方法有什么优势吗?
using (SqlConnection conn = new SqlConnection(conStr))
{
     using (SqlCommand command = new SqlCommand())
     {
        // dostuff
     } 
}

替代

using (SqlConnection conn = new SqlConnection(conStr))
{
     SqlCommand command = new SqlCommand();
     // dostuff
}

显然,如果您计划使用同一连接运行多个命令,则这很重要,因为关闭SqlDataReader比关闭并重新打开连接更有效(调用conn.Close(); conn.Open();也会释放连接)。
我看到很多人坚持认为未关闭SqlDataReader意味着留下了开放的连接资源,但这只适用于您不关闭连接的情况,不是吗?
5个回答

16

我认为在这里有两个规则需要遵循:

  1. 实现IDisposable接口的类应该被包装在using块内。
  2. 不应该依赖于一个类对IDisposable接口的实现而忽略第1条规则。

也就是说,即使您知道处理连接对象已经处理了其关联的命令对象的释放,您也不应该依赖这种行为。

顺便提一下,可以以更清晰的方式嵌套使用using块:

using (SqlConnection conn = new SqlConnection(conStr))
using (SqlCommand command = new SqlCommand())
{
    // dostuff
}

我会使用

SqlCommand command = conn.CreateCommand();

不要创建一个新的 SqlCommand,然后将其与连接关联,而是应该直接使用已有的 SqlCommand 对象。


2
正如我所说的那样。 - Chris

5
从技术上讲,关闭SqlConnection应该会销毁SqlDataReader正在使用的任何资源,因此不需要额外处理。反之亦然;如果使用CommandBehavior.CloseConnection创建了SqlDataReader,则无需DisposeSqlConnection

实际上,当一个类实现IDisposable时,您应该在完成后Dispose它。框架类的实现细节随时可能发生变化,除非文档明确概述了不需要Dispose实例的情况,否则未来的更改/更新可能导致您的代码出现资源泄漏。
这并不需要额外的工作-只需将其包装在using块中即可。

一个好的观点,尽管未来更改可能会破坏它,但涉及资源的释放确实是一项记录在案的功能。 - Brian
1
顺便提一下,有一些奇怪的情况,在 using 块中包装类会导致代码问题。我对此的回忆很模糊,但我认为这与 COM 组件有关,并且某些人认为这是一个 bug。 - Brian
@Brian:没有任何文档记录关闭 SqlConnection 会释放与 SqlDataReader 相关的所有资源。它会关闭连接 - 这是显而易见的,但你不能确定 SqlDataReader 是否使用其他资源。至于那些奇怪的情况,我猜你指的是生成的 WCF 客户端,它们有一个有问题的 IDisposable 实现。 - Aaronaught

1

在很多情况下可能并不是必需的,但这是最佳实践。没有理由让资源在不再有用的时候继续存在。使用using结构可以帮助确保这一点。


1
这并不是我问题的真正答案。我的意思是,在我的情况下,这个特定的问题并不适用,因为相关的资源无论如何都被释放了。这不仅仅是一项实现细节;它是一项已记录的实现细节。 - Brian

0

这不仅仅是你现在释放资源和垃圾回收稍后释放的问题吗?


我认为依赖垃圾回收器来释放SqlConnection是一件危险的事情。 - Brian
听起来很合理...我并没有判断哪个更好,只是陈述了我看到的差异。 - John
垃圾收集器不会释放或清理非托管资源,而这正是IDisposable接口的主要目的。 - tomosius

0

有所不同。如果您创建了2个这样的读取器而没有打开/关闭连接,但在使用第二个读取器之前没有处理掉第一个读取器,就会出现冲突,提示该连接已经与一个开放的读取器相关联。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接