任何人都可以告诉我CommandBehavior.CloseConnection
是什么以及将此作为com.ExecuteReader(CommandBehavior.CloseConnection)
中的参数传递的用途/好处是什么?
答案 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
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),也有好处:
防止IDataReader流入您的应用
如今,大多数应用程序将数据访问问题包含在存储库模式中,或者使用ORM来抽象数据访问 - 这通常会导致数据检索返回实体对象,而不是使用低级IDataReader
API本地工作#39;整个应用程序。
幸运的是,仍然可以使用延迟生成器(即返回IEnumerable<Entity>
的方法,并且仍然通过使用这样的模式保持对DataReader(以及连接)生命周期的控制(这是{{{ 1}}版本,但显然同步代码也可以工作,虽然是更多的线程饥饿)
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.
}
的代码仍然需要注意不要持有Enumerable(或它的Iterator)超过需要的时间,因为这将保留底层的Reader和Connection开。LazyQueryAllFoos
生成器in depth here - 一旦可枚举运行完成,或者抛出异常,外卖是使用块的yield return
将在yield迭代器块中完成(例如,网络故障),或者即使调用者没有完成对迭代器的迭代,也不会超出范围。