There are some articles which indicate that async database calls are bad idea in .NET.
On C# Async CTP, there is a System.Data.SqlClient.SqlCommand
extension called ExecuteReaderAsync
. I have some operations as below on my existing code:
var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["hubConnectionString"].ConnectionString; using (var conn = new SqlConnection(connectionString)) { using (var cmd = new SqlCommand()) { cmd.Connection = conn; cmd.CommandText = "sp$DetailsTagsGetAllFromApprovedPropsWithCount"; cmd.CommandType = System.Data.CommandType.StoredProcedure; conn.Open(); var reader = cmd.ExecuteReader(); while (reader.Read()) { //do the reading } conn.Close(); } }
There are several operations like this on my code. So, I am having thoughts on converting those to async.
But on the other hand, I am not seeing much attraction on this approach out there (maybe I am not looking at the right direction, who knows!).
So, is there any disadvantages of using this new async programming model here?
Edit:
Assuming I refactor the code as below:
public async Task<IEnumerable<Foo>> GetDataAsync() { List<Foo> foos = new List<Foo>(); var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["hubConnectionString"].ConnectionString; using (var conn = new SqlConnection(connectionString)) { using (var cmd = new SqlCommand()) { cmd.Connection = conn; cmd.CommandText = "sp$DetailsTagsGetAllFromApprovedPropsWithCount"; cmd.CommandType = System.Data.CommandType.StoredProcedure; conn.Open(); var reader = await cmd.ExecuteReaderAsync(); while (reader.Read()) { //do the reading //create foos } conn.Close(); } } return foos; }
As far as I understand from the await keyword, it converts the code, which is after it, as continuation. Also, when it hits the await keyword, it immediately returns to its caller regardless of the operation status. When it finishes it comes back and fire the continuation code.
This is what I have in mind.
ExecuteReaderAsync(CancellationToken) An asynchronous version of ExecuteReader(), which sends the CommandText to the Connection and builds a SqlDataReader. The cancellation token can be used to request that the operation be abandoned before the command timeout elapses.
Asynchronous calls are most useful when facing relatively infrequent large, expensive operations that could tie up response threads which could otherwise be servicing requests while the originator waits. For quick, common operations, async can slow things down.
I disagree with Ricka on this. Async DB commands are not only good, they are critical in achieving scale, throughput and latency. His objection about the ramp up time of the thread pool applies only to a web server that experiences low traffic volumes.
In a high traffic situation (which is the only one that matters), the thread pool won't have to wait for 'injecting' new threads. Doing the SQL Commands asynchronously is important not only from the point of view of web server requests/threads health, but also from the point of view of total request lifetime/latency: uncorrelated DB calls can be done in parallel, as opposed to sequentially. This alone results usually in dramatic improvements in the latency of the HTTP request as experienced by the user. In other words, your pages load faster.
A word of advice though: SQL Command is not truly asynchronous until you enable Asynchronous Processing=true
on the connection string. While this is not set (and by default is not, Edit: starting with .NET Framework < 4.5. Asynchronous Processing
is no longer required) your 'asyncronous' calls to BeginExecuteReader
are nothing but a sham, the call will launch a thread and block that thread. When true async processing is enabled in the connection string then the call is truly async and the callback is based on IO completion.
A word of caution: an async SQL command is completing as soon as the first result returns to the client, and info messages count as result.
create procedure usp_DetailsTagsGetAllFromApprovedPropsWithCount as begin print 'Hello'; select complex query; end
You've lost all benefits of async. The print
creates a result that is sent back to the client, which completes the async command and execution on the client resumes and continues with the 'reader.Read()'. Now that will block until the complex query start producing results. You ask 'who puts print
in the procedure?' but the print
may be disguised in something else, perhaps something as innocent looking as an INSERT
that executes without first issuing a SET NOCOUNT ON
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With