Can anyone tell me what is the CommandBehavior.CloseConnection
and what is the use/benefit of passing this as a parameter in com.ExecuteReader(CommandBeha
Re : What is the advantage of CommandBehavior.CloseConnection
?
Long lived data readers can be useful when you do not necessarily want to retrieve and materialize all data that the query would otherwise return, all-at-once. Although it is possible for your app to directly retain the reference to a long-lived Connection, this can lead to messy architecture, where Data Tier dependencies such as ISqlConnection
'bleeds' up into the business and presentation concerns of your application.
Lazy data retrieval (i.e. retrieving data only when it is needed) has the benefit that the caller can keep asking for more data until it has satisfied its data requirement - this is common in scenarios requiring data paging or take-while lazy evaluation, until some satisfying condition is met.
Long-lived connection / lazy data retrieval practices were arguably more commonplace in legacy architectures such as Fat-Client, where a user would scroll through data whilst keeping a connection open, however, there are still uses in modern code.
There is something of a trade off here: Although there are memory, and network resource overheads for both the App / Client side needed for the duration of the Reader (and Connection), and for keeping 'state' in the RDBMS database side (buffers, or even Cursors, if a PROC using Cursors is executed), there are also benefits:
Preventing IDataReader bleeding into your app
Nowadays, most apps wrap data access concerns into a Repository pattern or by using an ORM to abstract data access - this usually results in data retrieval returning Entity object(s) rather than working natively with low level IDataReader
API's throughout your app.
Fortunately, it is still possible to use a lazy generator (i.e. method returning IEnumerable
AND still retain control over the DataReader (and thus Connection) lifespan, by using a pattern such as this (this is an async
version, but obviously sync code would also work, albeit be more thread hungry)
public Task> 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 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.
}
Notes
LazyQueryAllFoos
still needs to be mindful not to hold onto the Enumerable (or an Iterator of it) for longer than needed, as this will keep the underlying Reader and Connection open.yield return
generators in depth here - the takeaway is that the finally
of using blocks will complete in yield iterator blocks once the enumerable has either run to completion, OR thrown an exception (e.g. network failure), OR even if the caller doesn't complete iterating the iterator and it goes out of scope.