![](/img/trans.png)
[英]Will ExecuteReader(CommandBehavior.CloseConnection) always close connection?
[英]What is the use/advantage of using CommandBehavior.CloseConnection in ExecuteReader()
任何人都可以告诉我什么是CommandBehavior.CloseConnection
以及将此作为com.ExecuteReader(CommandBehavior.CloseConnection)
的参数传递的用途/好处是什么?
在读取数据读取器时需要打开连接,并且希望尽快关闭连接。 通过在调用ExecuteReader
时指定CommandBehavior.CloseConnection
,可以确保代码在关闭数据读取器时关闭连接。
但是你应该已经立即处理你的连接(而不仅仅是关闭它们),在这种情况下,这样做最多只能获得边际(几乎肯定无法测量)的好处。
例如,此代码立即关闭其连接( 并执行处理它所需的任何其他工作),而不指定命令行为:
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
}
}
在创建连接但返回IDataReader
函数中使用CommandBehavior.CloseConnection
。 要非常小心Dispose()
的中IDbConnection
例外创建读者仅前:
IDataReader ReadAll()
{
var connection = new SqlConnection(connectionString);
try
{
connection.Open();
var command = connection.CreateCommand();
command.CommandText = "SELECT * FROM mytable";
var result = command.ExecuteReader(CommandBehavior.CloseConnection);
connection = null; // Don't dispose the connection.
return result;
}
finally
{
if (connection != null)
connection.Dispose();
}
}
如果不这样做,那么当您在循环中反复使用连接时,连接将保持“打开”状态,直到垃圾收集器将其选中,然后才会将其释放回ADO.net连接池被重复使用。 这意味着每次循环时,“打开”连接的代码将无法再次使用相同的代码(它尚未被释放回池中)。
因此,对于每个连续的循环迭代,ADO将需要从头开始创建另一个连接,最终,您可能会耗尽可用的连接。 根据GC关闭它所需的时间,您可能已经经历了大量的循环迭代,为每个循环迭代创建了一个新的不必要的连接,而所有这些未闭合和未使用的连接只是坐在那里。 如果使用CommandBehavior.CloseConnection,则在每个循环中,您将释放连接回池,并且下一次迭代可以重用它。 因此,您的流程将运行得更快,并且可以通过更少的连接逃脱。
我发现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);
}
第一个示例将关闭阅读器和连接。 第二个(事务性)仍将关闭读者,但不关闭连接。
Re: CommandBehavior.CloseConnection
什么优势?
当您不一定想要检索和实现查询本来会返回的所有数据时,长期存在的数据读取器非常有用。 虽然您的应用程序可以直接保留对长期连接的引用,但这可能会导致混乱的体系结构,其中数据层依赖性(如ISqlConnection
“ ISqlConnection
”到应用程序的业务和表示问题中。
延迟数据检索 (即仅在需要时检索数据)具有以下好处:调用者可以在满足其数据要求之前继续请求更多数据 - 这在需要数据分页或延迟评估的情况下是常见的,直到某些满足条件。
长期连接/延迟数据检索实践在Fat-Client等遗留架构中更为常见,用户可以在保持连接打开的同时滚动数据,但现代代码中仍然存在使用。
这里有一些权衡:虽然在读取器(和连接)期间需要内存和App / Client端的网络资源开销,并且在RDBMS数据库端保持“状态”(缓冲区) ,或者甚至是游标,如果执行使用游标的PROC),也有以下好处:
防止IDataReader流入您的应用程序
如今,大多数应用程序将数据访问问题包含在Repository模式中,或者使用ORM来抽象数据访问 - 这通常会导致数据检索返回Entity对象,而不是在整个应用程序中使用低级IDataReader
API本机工作。
幸运的是,仍然可以使用延迟生成器(即返回IEnumerable<Entity>
方法仍然保持对DataReader(以及连接)生命周期的控制,使用这样的模式(这是一个async
版本,但显然是同步的)代码也可以工作,虽然更加线程饥饿)
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.
}
笔记
LazyQueryAllFoos
的代码仍然需要注意不要将Enumerable(或它的Iterator)保持的时间超过需要的时间,因为这将使底层的Reader和Connection保持打开状态。 yield return
生成器 - 外卖的是,一旦可枚举运行完成,或者抛出异常(例如网络故障),或者即使调用者没有,也会在yield迭代器块中完成使用块的finally
结束不完全迭代迭代器,它超出了范围。 我建议阅读有关CommandBehaviour
枚举的MSDN文档:
CloseConnection - 执行命令时,关闭关联的DataReader对象时关闭关联的Connection对象。
将此与其他枚举项进行比较。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.