【问题标题】:Is it necessary to manually close and dispose of SqlDataReader?是否需要手动关闭和处置 SqlDataReader?
【发布时间】:2010-10-19 03:31:08
【问题描述】:

我在这里使用遗留代码,有很多 SqlDataReader 实例从未关闭或处置。连接已关闭,但我不确定是否需要手动管理阅读器。

这会导致性能下降吗?

【问题讨论】:

    标签: c# sql .net ado.net


    【解决方案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。这应该可以解决您的大部分问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-21
      • 2011-05-22
      • 1970-01-01
      • 2020-10-20
      • 1970-01-01
      • 1970-01-01
      • 2021-08-17
      相关资源
      最近更新 更多