是否需要手动关闭和处置 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 吗? 这很糟糕。如果你真的不忍心使用usings,那么在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?的主要内容,如果未能解决你的问题,请参考以下文章

我如何知道 UdpClient 是不是已关闭/处置?

C# SqlDataReader 关闭方法

是否需要手动销毁和关闭 java.lang.Process 的打开流?

如果在 SqlDataReader 之前关闭 SqlConnection 会发生啥?

处置和关闭有啥区别? [复制]

SqlDataReader的关闭问题,报错:“阅读器关闭时尝试调用 Read 无效”