为什么还要调用SqlClient.SqlDataReader Close()方法?

时间:2008-10-30 14:58:57

标签: .net-3.5 sqlconnection

SqlClient.SqlDataReader是否是.NET托管对象? 为什么我们必须调用Close()方法显式关闭一个打开的连接? 不应该超出这个对象的范围自动关闭这个? 垃圾收集器不应该清理干净吗?

请帮助我了解这里的最佳做法。

我看到了一个相关的问题here,它进一步说明了我对网络应用程序的问题。问题是我们的连接不畅。详细错误在这里:

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 个答案:

答案 0 :(得分:35)

当然,它会在超出范围时收集(如果它们没有其它参考)。收集它时,它将通过Dispose()方法关闭。但是,你真的不知道GC什么时候会解除分配;如果你不关闭你的读者,你很快就会耗尽与数据库的可用连接。

进一步阅读

~William Riley-Land

答案 1 :(得分:17)

@Lieutenant Frost

  

作为我们商店的一项规则,我们明确表示   包装所有数据库调用   尝试...最后阻止,最后   捕捉和关闭数据部分   连接。值得一点点   努力拯救自己的专业   排除故障。

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

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

即使有异常,using块在离开作用域时立即调用Dispose。

答案 2 :(得分:10)

一个好的做法(只要您不重用连接)是将命令行为添加到SqlDataReader以在处理时关闭连接:

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

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

如前所述,如果您计划在同一方法中重新使用数据库连接进行其他操作,则不希望这样做。

答案 3 :(得分:4)

我想其他人都说过这个,但我希望它清楚:

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

您的应用需要在多个级别上“玩得很好”。 关闭连接有助于您做到这一点。 让我们来看看其中的几个级别。

1:您不依赖于垃圾收集。 理想情况下,垃圾收集不应该存在。但确实如此。 但大多数人肯定不应该依赖它。

2:您没有持有数据库连接。 虽然连接通常是汇集的,但您发现存在限制。 保持这个时间超过必要的时间会使你的应用成为糟糕的苹果。

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

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

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

答案 4 :(得分:3)

如果你没有明确地关闭它,那么它就在那里等待垃圾收集器“收集”它......只有在那之后它才会被释放回ADO.Net连接池以供另一个重用Connection.Open请求,所以在所有介入时间内,任何其他连接请求都必须创建一个全新的请求,即使有一个非常好的,可以使用未使用的... ...

根据GC运行之前的时间长度以及执行的数据库请求数量,可能会耗尽与数据库的可用连接。

但是Command.ExecuteReader()方法中有一个名为CommandBehavior的可选参数。这是一个枚举,其值为: CommandBehavior.Default,CommandBehavior.SingleResult,CommandBehavior.SchemaOnly,CommandBehavior.KeyInfo,CommandBehavior.SingleRow,CommandBehavior.SequentialAccess和CommandBehavior.CloseConnection

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

不幸的是,当数据阅读器关闭时,默认情况下不是关闭连接,但是如果希望方法在您使用时立即将连接释放回池,则可以(并且应该)将此参数作为CommandBehavior.CloseConnection传递完成它......

答案 5 :(得分:2)

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

这对您来说是个问题的原因是垃圾收集器在超出范围时不会为每个对象运行。不那么频繁地收集更多物品会更有效率。因此,如果你等待收集器处理你的对象(是的,如果你最终会实现idisposable),你可能会打开一些比你意识到的更长的数据库连接。

答案 6 :(得分:2)

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

作为我们商店的一项规则,我们在Try ... Finally块中显式包装所有数据库调用,最后一部分捕获并关闭数据连接。值得付出一点努力,为自己避免重大的故障排除问题。

答案 7 :(得分:1)

这不是Connection的问题,而是由SqlDataReader保存的SQL Cursor。如果你尝试在不关闭第一个的情况下打开第二个,它将抛出异常。