是否需要手动关闭和处置 SqlDataReader?
Posted
技术标签:
【中文标题】是否需要手动关闭和处置 SqlDataReader?【英文标题】:Is it necessary to manually close and dispose of SqlDataReader? 【发布时间】:2010-10-19 03:31:08 【问题描述】:我在这里使用遗留代码,有很多 SqlDataReader
实例从未关闭或处置。连接已关闭,但我不确定是否需要手动管理阅读器。
这会导致性能下降吗?
【问题讨论】:
【参考方案1】:尽量避免使用这样的阅读器:
SqlConnection connection = new SqlConnection("connection string");
SqlCommand cmd = new SqlCommand("SELECT * FROM SomeTable", connection);
SqlDataReader reader = cmd.ExecuteReader();
connection.Open();
if (reader != null)
while (reader.Read())
//do something
reader.Close(); // <- too easy to forget
reader.Dispose(); // <- too easy to forget
connection.Close(); // <- too easy to forget
相反,将它们包装在 using 语句中:
using(SqlConnection connection = new SqlConnection("connection string"))
connection.Open();
using(SqlCommand cmd = new SqlCommand("SELECT * FROM SomeTable", connection))
using (SqlDataReader reader = cmd.ExecuteReader())
if (reader != null)
while (reader.Read())
//do something
// reader closed and disposed up here
// command disposed here
//connection closed and disposed here
using 语句将确保正确处理对象并释放资源。
如果您忘记了,那么您将把清理工作留给垃圾收集器,这可能需要一段时间。
【讨论】:
您不需要任何示例中的 .Close() 语句:它由 .Dispose() 调用处理。 我不明白“cmd.ExecuteReader”之后阅读器何时为空? 可能想检查它是否为 .HasRows 而不是 null。 @Andrew 如果ExecuteReader抛出异常,怎么会返回null? @JohH:示例中的 while (reader.Read()) 完成与 .HasRows 相同的操作,并且无论如何您都需要 .Read 以将阅读器前进到第一行。【参考方案2】:请注意,释放使用 SqlCommand.ExecuteReader() 实例化的 SqlDataReader 将不会关闭/释放底层连接。
有两种常见的模式。首先是在连接范围内打开和关闭阅读器:
using(SqlConnection connection = ...)
connection.Open();
...
using(SqlCommand command = ...)
using(SqlDataReader reader = command.ExecuteReader())
... do your stuff ...
// reader is closed/disposed here
// command is closed/disposed here
// connection is closed/disposed here
有时让数据访问方法打开连接并返回读取器很方便。在这种情况下,使用 CommandBehavior.CloseConnection 打开返回的阅读器很重要,这样关闭/处置阅读器将关闭底层连接。模式看起来像这样:
public SqlDataReader ExecuteReader(string commandText)
SqlConnection connection = new SqlConnection(...);
try
connection.Open();
using(SqlCommand command = new SqlCommand(commandText, connection))
return command.ExecuteReader(CommandBehavior.CloseConnection);
catch
// Close connection before rethrowing
connection.Close();
throw;
而调用代码只需要这样处理读者:
using(SqlDataReader reader = ExecuteReader(...))
... do your stuff ...
// reader and connection are closed here.
【讨论】:
在方法返回 SqlDataReader 的第二个代码 sn-p 中,该命令未处理。可以吗?可以处理命令(将其包含在 using 块中)然后返回阅读器吗? @alwayslearning 这正是我所拥有的场景......当您将 SqlDataReader 返回给调用者时,您可以关闭/处理 SqlCommand 吗? 这很糟糕。如果你真的不忍心使用using
s,那么在catch 之后在finally
块中调用dispose。按照这种编写方式,成功的命令永远不会被关闭或释放。
@smdrager,如果您仔细阅读答案,他正在谈论一种返回读者的方法。如果您使用 .ExecuteReader(CommandBehavior.CloseConnection);然后通过处置 READER,连接将被关闭。因此调用方法只需要将结果读取器包装在 using 语句中。 using(var rdr = SqlHelper.GetReader()) //... 如果您在 finally 块中关闭它,那么您的阅读器将无法读取,因为连接已关闭。
@ganders - 回到这个旧帖子:是的,你可以并且可能应该处理 SqlCommand - 更新了这样做的示例。【参考方案3】:
为了安全起见,请将每个 SqlDataReader 对象包装在 using statement 中。
【讨论】:
很公平。但是,如果没有 using 语句,它是否真的会对性能产生影响? using 语句与将 DataReader 代码包装在 try..finally... 块中相同,在 finally 部分中使用 close/dispose 方法。基本上,它只是“保证”该对象将被正确处理。 这直接来自我提供的链接:“using 语句确保即使在调用对象上的方法时发生异常,也会调用 Dispose。” Continued... "您可以通过将对象放在 try 块中,然后在 finally 块中调用 Dispose 来获得相同的结果;实际上,这就是编译器翻译 using 语句的方式。”【参考方案4】:只需用“using”语句包装您的 SQLDataReader。这应该可以解决您的大部分问题。
【讨论】:
以上是关于是否需要手动关闭和处置 SqlDataReader?的主要内容,如果未能解决你的问题,请参考以下文章
是否需要手动销毁和关闭 java.lang.Process 的打开流?