在ExecuteReader()中使用CommandBehavior.CloseConnection的用途/优点是什么

时间:2011-04-16 20:06:23

标签: c# ado.net

任何人都可以告诉我CommandBehavior.CloseConnection是什么以及将此作为com.ExecuteReader(CommandBehavior.CloseConnection)中的参数传递的用途/好处是什么?

6 个答案:

答案 0 :(得分:19)

您在阅读数据阅读器时需要打开连接,并且希望尽快关闭连接。通过在调用CommandBehavior.CloseConnection时指定ExecuteReader,您可以确保代码在关闭数据阅读器时关闭连接。

但你应该已经 立即处理您的连接(而不仅仅是关闭它们),在这种情况下,最好是边缘(几乎肯定无法衡量)这样做有好处。

例如,此代码立即关闭其连接(执行处理它所需的任何其他工作),而不指定命令行为:

using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlCommand command = new SqlCommand(commandText, connection))
{
    connection.Open();
    using (SqlDataReader reader = command.ExecuteReader()) 
    {
        while (reader.Read())
           // Do something with the rows
    }
}

答案 1 :(得分:6)

在创建连接但返回CommandBehavior.CloseConnection的函数中使用IDataReader。在创建阅读器之前,要非常小心Dispose()的{​​{1}}例外

IDbConnection

答案 2 :(得分:2)

如果不这样做,那么当您在循环中反复使用连接时,连接将保持“打开”状态,直到垃圾收集器将其拾取为止,然后才会将其释放回ADO。要重新使用的网络连接池。这意味着每次循环时,“打开”连接的代码将无法再次使用相同的代码(它尚未被释放回池中)。
 因此,对于每个连续的循环迭代,ADO将需要从头开始创建另一个连接,最终,您可能会耗尽可用的连接。根据GC关闭它所需的时间,您可能已经经历了大量的循环迭代,为每个循环迭代创建了一个新的不必要的连接,而所有这些未闭合和未使用的连接只是坐在那里。  如果使用CommandBehavior.CloseConnection,则在每个循环中,您将释放连接回池,并且下一次迭代可以重用它。因此,您的流程运行得更快,并且可以通过更少的连接逃脱。

答案 3 :(得分:0)

我建议您阅读CommandBehaviour Enumeration

的MSDN文档
  

CloseConnection - 执行命令时,关闭关联的DataReader对象时关闭关联的Connection对象。

将此与其他枚举项进行比较。

答案 4 :(得分:0)

我发现CommandBehavior.CloseConnection的最佳用途是当你想要编写足够灵活的代码以便在事务中使用时(这意味着所有查询都有共享连接)。考虑:

public DbDataReader GetReader(DbCommand cmd, bool close = true)
{
    if(close)
        return cmd.ExecuteReader(CommandBehavior.CloseConnection);
    return cmd.ExecuteReader();
}

如果您正在将读取操作作为更大事务的一部分运行,则需要将false传递给此方法。在任何一种情况下,您仍应使用using语句进行实际读取。

不在交易中:

using(var reader = GetReader(cmd, true))
{
    while(reader.Read())
        ...
}

在交易中,可能会检查是否存在记录:

bool exists = false;
using(var reader = GetReader(cmd, false))
{
    if(reader.Read())
        exists = reader.GetBoolean(0);
}

第一个示例将关闭阅读器和连接。第二个(事务性)仍将关闭读者,但不关闭连接。

答案 5 :(得分:0)

回复:CommandBehavior.CloseConnection的优势是什么?

如果您不一定想要检索和实现查询本来会返回的所有数据,那么长寿命数据读取器可能非常有用。虽然您的应用程序可以直接保留对长期连接的引用,但这可能会导致混乱的体系结构,其中数据层依赖性(例如ISqlConnection'出血'了解您的应用程序的业务和演示问题。

延迟数据检索(即仅在需要时检索数据)具有以下好处:调用者可以在满足其数据要求之前不断要求更多数据 - 这在需要数据的场景中很常见寻呼或延迟懒惰评估,直到满足一些满意的条件。

长期连接/延迟数据检索实践在Fat-Client等传统架构中更为常见,用户可以在保持连接打开的同时滚动数据,但是,现代代码中仍然存在使用。

这里存在一些折衷:虽然在读取器(和连接)的持续时间内需要App / Client端的内存和网络资源开销,并且用于保持状态&#39 ;在RDBMS数据库端(缓冲区,甚至Cursors,如果执行使用Cursors的PROC),也有好处:

  • 消费应用和数据库之间的网络IO减少,因为只检索了所需的数据
  • 应用程序内存开销减少,因为不需要的数据不被检索(如果在DataReader之上使用对象抽象,这也可能节省不必要的反序列化/实现实体/ DTO / POCOs)
  • 通过不实时查询查询中的所有数据,它允许应用程序在通过阅读器时释放不再需要的内存(例如DTO&#39)

防止IDataReader流入您的应用

如今,大多数应用程序将数据访问问题包含在存储库模式中,或者使用ORM来抽象数据访问 - 这通常会导致数据检索返回实体对象,而不是使用低级IDataReader API本地工作#39;整个应用程序。

幸运的是,仍然可以使用延迟生成器(即返回IEnumerable<Entity>的方法,并且仍然通过使用这样的模式保持对DataReader(以及连接)生命周期的控制(这是{{{ 1}}版本,但显然同步代码也可以工作,虽然是更多的线程饥饿)

async

备注

  • 与SqlConnections和DataReader一样,调用public Task<IEnumerable<Foo>> LazyQueryAllFoos() { var sqlConn = new SqlConnection(_connectionString); using (var cmd = new SqlCommand( "SELECT Id, Col2, ... FROM LargeFoos", sqlConn)) { await mySqlConn.OpenAsync(); var reader = cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection); // Return the IEnumerable, without actually materializing Foos yet // Reader and Connection remain open until caller is done with the enumerable return LazyGenerateFoos(reader); } } // Helper method to manage lifespan of foos private static IEnumerable<Foo> GenerateFoos(IDataReader reader) { // Lifespan of the reader is scoped to the IEnumerable using(reader) { while (reader.Read()) { yield return new Foo { Id = Convert.ToInt32(reader["Id"]), ... }; } } // Reader is Closed + Disposed here => Connection also Closed. } 的代码仍然需要注意不要持有Enumerable(或它的Iterator)超过需要的时间,因为这将保留底层的Reader和Connection开。
  • Jon Skeet分析LazyQueryAllFoos生成器in depth here - 一旦可枚举运行完成,或者抛出异常,外卖是使用块的yield return将在yield迭代器块中完成(例如,网络故障),或者即使调用者没有完成对迭代器的迭代,也不会超出范围。
  • 在C#6中,异步代码当前也不能使用yield return,因此需要将helper方法从异步查询中分离出来(但我可以认为帮助器可以移动到Local Function中)。事情可能在未来发生变化,例如IAsyncEnumerator