3

I am porting some old ASP.NET code to .NET 4.5. I am used to using SqlConnection/SqlCommand/SqlDataReader the old fashioned way. Now I am looking at using async primitives in hopes of better performance.

Here is my old code for opening a connection:

    static DBWrapper Open(string connKey) {
        string connStr = WebConfigurationManager.ConnectionStrings[connKey].ConnectionString;
        SqlConnection c = new SqlConnection(connStr);
        c.Open();
        DBWrapper retVal = new DBWrapper();
        retVal._c = c;
        return retVal;
    }

Class DBWrapper is just a thin wrapper over SqlConnection that has some extra methods I need.

Here is how I converted to the new async style:

    static async Task<DBWrapper> Open(string connKey) {
        string connStr = WebConfigurationManager.ConnectionStrings[connKey].ConnectionString;
        SqlConnection c = new SqlConnection(connStr);
        var v = c.OpenAsync();
        DBWrapper retVal = new DBWrapper();
        await v;
        retVal._c = c;
        return retVal;
    }

Basically, while the connection is being opened, I am able to create a new DBWrapper object concurrently.

What I am confused is if this function really performs better or worse. The overhead of task creation and waiting on it may be much more than the time it takes to create a new DBWrapper object. Whether or not I use async, I am ultimately returning a fully built connection object anyway.

Here is a method that returns the list of users from the database:

 List<MyUsers> getUsers() {
     using(SqlCommand cmd ...) {
       using(SqlDataReader reader ...) {
          while(reader.Read() {
             ... Fill the list
          }
       }
 }

I can convert this method as well into async style as well:

while (await reader.ReadAsync())
  ... Fill the list
}

Once again I don't see any performance gains. In fact, in the second example, I don't even do anything else concurrently while waiting on ReadAsync().

Is this the correct approach or is there a better way to use async paradigm with database connections and queries?

Peter
  • 11,260
  • 14
  • 78
  • 155

1 Answers1

20

Keep in mind that async does not mean "faster"; it means "concurrent". That is, the point of async is that you can do something else while the operation is going on. In a UI app, this "something else" is normally "responding to user input"; the primary benefit of async for UI apps is responsiveness. For ASP.NET apps, it works a little differently...

If you use async "all the way up", then the primary benefit of async on ASP.NET apps is scalability. By "all the way up", I mean if your List<MyUsers> getUsers() method became Task<List<MyUsers>> getUsersAsync(), and the method that called it becomes async, and so on and so forth all the way out to your MVC action / WebPage method / whatever (which is also async), then you're "async all the way".

In that scenario, then you get a scalability benefit. Note that each individual operation will not run any faster, but your application as a whole will scale better. When await yields to the ASP.NET runtime, the request thread is freed up to handle other requests while that database connection is receiving the next row of data. With the synchronous code, any time your app is waiting on I/O while talking to the database, it's blocking a thread (which is doing nothing); async frees up that thread while waiting for the I/O.

So the app as a whole can scale better, since it is making better use of the thread pool threads.

It's true that sometimes you can get gains by exploiting concurrency within a request; for example, if one request had to access the database and also call some web API, then you could start both of them and use await Task.WhenAll(...) to (asynchronously) wait for both results. In your first code example, you're doing the db connection and an object creation concurrently, but I suspect you won't really see any benefit in that situation unless your constructor is really computationally expensive. If you have two I/O operations or a real CPU operation, then async could help you do concurrency within a request, but the primary use case is to improve scalability of the application as a whole.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Thank you for an elaborate answer. Just to be sure, you feel that the overhead of async methods is justifiable in the above two specific cases. Is this correct? – Peter Aug 13 '14 at 04:45
  • 2
    The overhead of async is pretty much negligible. If you have a scalable backend (e.g., Azure SQL), then I'd use async for any I/O. The only place I wouldn't use async is if 1) the backend isn't scalable (e.g., a single instance of SQL Server) **and** 2) most requests hit the backend **and** 3) you already have a synchronous solution. In that case, *converting* to async wouldn't gain you any benefits. But if your backend can scale, or if there are a lot of requests that do other things (not hitting the backend), or if you're starting a new solution, then use async. – Stephen Cleary Aug 13 '14 at 12:16
  • @StephenCleary very thoughtful guidance, but what if each async task is hitting a single instance of SQL Server but the filegroups for the tables involved are on different disks? Ex: Main DB and History DB, or Front Office DB and Back Office DB. I would expect scaling to only be limited by the network adapter for the attached storage. Everything is going SSD in the cloud nowadays. – John Zabroski Jul 28 '17 at 12:30
  • @JohnZabroski Sure, in that case you may want to consider converting to async. I always write new code as async - there's nothing wrong with that - but whether it's worth it to *convert* to async is another question. – Stephen Cleary Jul 28 '17 at 12:45
  • Why *always*? It could end up adding overhead. – John Zabroski Jul 28 '17 at 12:51
  • @JohnZabroski I/O operations are asynchronous by nature. Making them synchronous takes up more resources. – Stephen Cleary Jul 28 '17 at 13:25
  • @StephenCleary I resonate with your answer, but after getting this [other point of view](https://stackoverflow.com/questions/55091367/benefits-of-async-sqlclient-methods/55091599#comment96930488_55091599) I'm not sure anymore... would you agree with David's comment? thanks. – yv989c Mar 11 '19 at 19:56
  • 1
    @yv989c: Not in the general case. In the specific case of having a single database backend, then sure, scaling your web server isn't going to help the scalability of your system as a whole. But in a cloud scenario with, e.g., Azure SQL backend, then `async` is perfectly helpful. See [Asynchronous Code is Not a Silver Bullet in Async ASP.NET](https://msdn.microsoft.com/en-us/magazine/dn802603.aspx). – Stephen Cleary Mar 11 '19 at 20:41
  • @StephenCleary thanks a lot for that link to your article, it gave me another perspective; Rick Anderson's article was useful too despite being from 2012. In summary, I think the answer about using synchronous vs asynchronous for database calls will be "it depends", but now I have a better understanding; we always need to to factor in things like: web server resources, network latency to the SQL server instance, possible table locks (or hotspots), etc. that are application specific; that's the only way to make an educated decision on when we will benefit from async in this context. – yv989c Mar 12 '19 at 14:58
  • @yv989c: Yes, this is something that needs to be considered when considering async-ifying existing synchronous code. For new code/projects, there's no disadvantages to using `async` from the beginning. – Stephen Cleary Mar 12 '19 at 20:28