是否需要手动关闭并处置SqlDataReader?


90

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

这会导致性能下降吗?

Answers:


124

尽量避免使用这样的读者:

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语句将确保正确处置对象和释放资源。

如果您忘记了,则将清理工作留给垃圾收集器,这可能需要一段时间。


24
在任何一个示例中都不需要.Close()语句:它由.Dispose()调用处理。
Joel Coehoorn

7
可能想检查它是否具有.HasRows而不是null。
JonH

3
@Andrew如果ExecuteReader引发异常,如何返回null?
csauve 2012年

7
@JohH:示例中的while(reader.Read())与.HasRows的功能相同,并且您仍然需要始终进行.Read才能将阅读器前进到第一行。
csauve 2012年

1
@csauve是的,我想它一定不能返回null。我不确定为什么要查看SqlDataReader变量的值。
2012年

53

请注意,处置使用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的第二个代码片段中,未放置命令。可以,并且可以处置命令(将其包含在using块中)然后返回阅读器吗?
alwayslearning

@alwayslearning正是我所遇到的情况……在将SqlDataReader返回给调用方时,是否可以关闭/处置SqlCommand?
雄狮2012年

1
这不好。如果您真的不愿意使用usings,则finally {}在catch之后在块中调用dispose 。编写方式,成功的命令将永远不会关闭或处置。
smdrager 2012年

2
@smdrager,如果您仔细阅读答案,那么他正在谈论一种返回读者的方法。如果使用.ExecuteReader(CommandBehavior.CloseConnection); 然后通过放置READER,将关闭连接。因此,调用方法仅需要将生成的阅读器包装在using语句中。using(var rdr = SqlHelper.GetReader()){// ...}如果您在finally块中将其关闭,则由于连接已关闭,您的阅读器将无法读取。
Sinaesthetic 2012年

@ganders-返回此旧文章:是的,您可以并且可能应该处置SqlCommand-更新了示例以执行此操作。

11

为了安全起见,将每个SqlDataReader对象包装在using语句中


很公平。但是,如果没有using语句,它实际上会对性能产生影响吗?
乔恩·奥恩比

using语句与将DataReader代码包装在try..finally ...块中相同,在finally部分中使用close / dispose方法。基本上,只是“保证”对象将被正确处理。
托德(Todd)

这直接来自我提供的链接:“ using语句确保即使在调用对象的方法时发生异常,也将调用Dispose。”
Kon

5
续...“您可以通过将对象放在try块中,然后在finally块中调用Dispose来获得相同的结果;实际上,这是编译器翻译using语句的方式。”
Kon

5

只需使用“ using”语句包装您的SQLDataReader。那应该解决您的大多数问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.