代码之家  ›  专栏  ›  技术社区  ›  Julius A

为什么还要调用sqlclient.sqldatareader close()方法?

  •  17
  • Julius A  · 技术社区  · 17 年前

    sqlclient.sqldatareader是否为.NET托管对象? 为什么我们必须显式地调用close()方法来关闭打开的连接? 这样的对象不应该超出范围自动关闭它吗? 垃圾收集器不应该把它清理干净吗?

    请帮助我了解这里的最佳实践是什么。

    我看到了一个相关的问题 here 它进一步说明了我在Web应用程序中遇到的问题。问题是,我们的连接中断了。详细错误如下:

    Exception: System.InvalidOperationException
    Message: Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
    Source: System.Data 
    
    at  System.Data.SqlClient.SqlConnectionPoolManager.GetPooledConnection(SqlConnectionString options, Boolean& isInTransaction)
    at  System.Data.SqlClient.SqlConnection.Open()
    

    为了解决这个问题,我必须显式地关闭所有的sqldatareader对象。

    我正在使用.NET Framework 3.5

    8 回复  |  直到 11 年前
        1
  •  35
  •   wprl    16 年前

    当然,它将在超出范围时收集(如果它们不是对它的其他引用)。收集后,将通过Dispose()方法关闭它。但是,您永远不会真正知道GC何时会解除分配;如果您不关闭读卡器,那么很快就会用完与数据库的可用连接。

    进一步阅读

    ~威廉·莱利·兰德

        2
  •  17
  •   Pavel Chuchuva grapeot    16 年前

    @弗罗斯特中尉

    我们店里的规矩是,我们 将所有数据库调用包装在 尝试…最后阻止,最后 段捕获和关闭数据 连接。值得一点点 努力拯救自己的专业 排除头痛。

    我有一个类似的规则,但我要求实现IDisposable的对象使用“using”块。

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

    在离开作用域时,即使出现异常,using块调用也会立即释放。

        3
  •  10
  •   Jeffrey Harrington    17 年前

    一个好的做法(只要不使用连接)是将命令行为添加到sqldatareader,以便在释放连接时关闭连接:

    SqlDataReader rdr = cmd.ExecuteReader( CommandBehavior.CloseConnection );
    

    添加此项将确保在关闭sqldatareader对象(或垃圾收集)时关闭与数据库的连接。

    不过,正如我之前所说,如果您计划在同一方法中为另一个操作重新使用数据库连接,则不希望这样做。

        4
  •  4
  •   Will Rickards    17 年前

    我想其他人都这么说了,但我想说清楚:

    超出范围并不意味着立即进行垃圾收集。

    您的应用程序需要在多个级别上“运行良好”。 关闭连接有助于实现这一点。 让我们来研究其中的几个层次。

    1:你不依赖垃圾收集。 理想情况下,垃圾收集不需要存在。但确实如此。 但最肯定的是你不应该依赖它。

    2:您没有保持数据库连接。 虽然连接通常是合并的,但正如您所发现的,这是有限制的。 持有这个比需要的时间长,使你的应用程序成为坏苹果。

    3:您没有生成网络流量。 每个数据库连接本质上都是一个TCP连接。 保持它的开放可能会产生沿着 你还在吗?对。 小流量是的,但在拥挤的网络上,这是不好的做法。 而SQL Server本身正在使用资源来保持连接的活动性。 其他试图访问该SQL Server的人可以更好地利用这些资源。

    在考虑数据库访问时,还必须考虑网络资源。 一些获取数据的方法是不好的,因为它们可以带来不必要的东西。ADO的早期版本因这种类型的东西而臭名昭著。只需要数据时返回架构信息。当然,只有几个连接,这不是问题。但从什么时候开始,任何数据库只有几个连接。所以想想这些东西,不要滥用资源。

    当你的网络应用只有100个用户时,你可能不在乎。 但10万呢?总是考虑当你缩放时会发生什么。

        5
  •  3
  •   Charles Bretana    12 年前

    如果您没有显式地关闭它,那么它就在那里等待垃圾收集器“收集”它…只有在这种情况发生后,它才会被释放回ADO.NET连接池,以供另一个连接重用。打开请求,因此在所有的干预时间内,任何其他连接请求都必须创建一个全新的连接,即使有一个非常好的未使用的连接可以使用…

    根据GC运行前的时间和执行的数据库请求数,可能会耗尽到数据库的可用连接。

    但command.executereader()方法上有一个可选参数,称为commandBehavior。这是一个枚举,其值为: commandBehavior.default、commandBehavior.singleResult、commandBehavior.schemaOnly、commandBehavior.keyifo、commandBehavior.singleRow、commandBehavior.sequentialAccess和commandBehavior.closeConnection

    此枚举具有FlagsAttribute属性,该属性允许其成员值按位组合。它是与此相关的最后一个值(commandBehavior.closeConnection)。它告诉命令对象在数据读取器关闭时关闭连接。 http://msdn.microsoft.com/en-us/library/system.data.commandbehavior.aspx

    不幸的是,默认情况下不会在关闭数据读取器时关闭连接,但您可以(并且应该)将此参数作为commandBehavior.closeConnection传递,当您希望方法在完成连接后立即释放回池的连接时…

        6
  •  2
  •   Joel Coehoorn    17 年前

    术语“托管代码”所指的“托管”资源是内存。就是这样。任何其他稀缺资源都需要用一次性模式包装,包括数据库连接。

    这对您来说是一个问题的原因是垃圾收集器不会在每个对象超出范围时运行。收集更多物品的频率越低,效率就越高。因此,如果等待收集器处理对象(是的,如果实现IDisposable,它最终会实现),那么您可能拥有比您所认识到的要长得多的数据库连接。

        7
  •  2
  •   Lieutenant Frost    17 年前

    还要考虑当抛出异常时会发生什么-如果突然被强制退出正在执行的代码,您永远不知道连接是否会关闭。

    作为我们车间的一个规则,我们将所有数据库调用显式包装在一个try…finally块中,finally部分捕获并关闭数据连接。这是值得一点点努力,以节省自己一个主要的故障排除头痛。

        8
  •  1
  •   James Curran    17 年前

    问题不在于连接,而在于SQLDataReader持有的SQL光标。如果试图打开第二个而不关闭第一个,则会引发异常。

    推荐文章