38

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.

bkaid
  • 51,465
  • 22
  • 112
  • 128
tugberk
  • 57,477
  • 67
  • 243
  • 335
  • Although the accepted answer is perfect, i would like to add my example. In my website, i made all the action methods as Task and made all the db calls async. The website really started loading faster – It's a trap Sep 21 '16 at 09:07

2 Answers2

63

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.

p.campbell
  • 98,673
  • 67
  • 256
  • 322
Remus Rusanu
  • 288,378
  • 40
  • 442
  • 569
  • *"Command is not truly asynchronous until you enable Asynchronous Processing=true on the connection string."* Is it still applicable if I use the new async programming model as I indicate on my question? – tugberk Feb 24 '12 at 17:24
  • 6
    Good lord who make design decisions like this? These gotchas are absurd. – Chris Marisic Feb 24 '12 at 17:48
  • @tugberk: I'm pretty sure `ExecuteReaderAsync` is nothing but a wrapper around `BeginExecuteReader`/`EndExecuteReader`, therefore the connection string requirement applies to it as well. – Remus Rusanu Feb 24 '12 at 17:57
  • I don't think so (while I am not sure). New async programming model is a lot different I think. First of all, it is Task based. – tugberk Feb 24 '12 at 18:04
  • 5
    Yes, but the underlying async capabilities come from the `SqlClient` component. The Async team is building the language and .Net Framework capabilities leveraging the existing `IAsyncResult` features, not reinventing each one from scratch. – Remus Rusanu Feb 24 '12 at 18:41
  • yes, right. It's what it does: `public static Task ExecuteReaderAsync(this SqlCommand source) { return Task.Factory.FromAsync(new Func(source.BeginExecuteReader), new Func(source.EndExecuteReader), null); }` – tugberk Feb 24 '12 at 19:29
  • 6
    BTW as of .Net 4.5 there is new [SqlDataReader.ReadAsync](http://msdn.microsoft.com/en-us/library/hh223681(v=vs.110).aspx) method. – Remus Rusanu Apr 04 '12 at 14:39
  • 9
    Beginning in the .NET Framework 4.5, these methods no longer require Asynchronous Processing=true in the connection string. http://msdn.microsoft.com/en-us/library/hh211418.aspx – chengbo Jan 23 '14 at 21:41
  • Excellent, i'm beginning to use async methods for my database calls. – Hackerman Jun 02 '16 at 14:50
0

I notice that the following question wasn't answered:

So, is there any disadvantages of using this new async programming model here?

The disadvantage which is very minimal (minor cpu/minor memory afaik), is that because there is the possibility of any code running after an await statement may run on a separate thread, a state-machine exists to store the state of the currently running thread so that the continuation of work can be handled on another thread. You can read more about the await/async statemachine on Dixin's Blog - Understanding C# async / await (1) Compilation.

Erik Philips
  • 53,428
  • 11
  • 128
  • 150