14

I have an Index action in ASP.net MVC controller. This action, calls (among other things) a private action that do a count on a SQL table with large set of rows. The returned number will be inserted in a view bag property.

public ActionResult Index() 
{
    // do things
    ViewBag.NumberOfRows = NumberOfRows();
    return View();
}

private string NumberOfRows()
{
    // sql connection and row count
    return numberOfRows;
}

This works but I can't see the Index page until everything is executed, even the row count. I would, instead, Index action to immediately complete, even if private function hasn't been completed yet. Than when count has been completed set a value to the view bag property. Right now I've done this:

private async Task<string> NumberOfRows()
{
    SqlConnection connection = new SqlConnection(connString);
    SqlCommand cmd = new SqlCommand();
    SqlDataReader reader;

    cmd.CommandText = "SELECT SUM (row_count) FROM sys.dm_db_partition_stats WHERE object_id=OBJECT_ID('aTable') AND (index_id=0 or index_id=1)";
    cmd.CommandType = CommandType.Text;
    cmd.Connection = connection;

    await connection.OpenAsync();

    reader = await cmd.ExecuteReaderAsync();
    string numberOfRows = "N/A";
    while (await reader.ReadAsync())
    {
        numberOfRows = reader.GetInt64(0).ToString();
    }

    connection.Close();

    return numberOfRows ;
}

public async Task<ActionResult> Index(FormCollection form){
    // do things;
    ViewBag.NumberOfRows = await NumberOfRows();
    return View();
}

This works. But is this really async? Am I miss something, there are other way of doing this?

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
BAD_SEED
  • 4,840
  • 11
  • 53
  • 110
  • 3
    Please note that `SqlConnection`, `SqlCommand` and `SqlDataReader` all implement the `IDisposable` interface, so you should consider using the `using` statement - this works well with async operations in it as well! Also see this question for more information: http://stackoverflow.com/q/16985876/927511 – Dennis Jul 06 '15 at 11:39

3 Answers3

16

Its async call but one important thing to understand here is when you make your controller action async in that case : thread(of asp.net thread pool) which handling request return to thread pool (asp.net request thread pool ).

That means it release thread of thead pool to handle more request (It means async controller action just help to handle more request it doesnt mean that it decrease your time of processing, It just make your server more responsive). once operation under async/await is get completed new thread from request thread pool does further processing.

If you want real async page i.e. want to make your page more responsive I suggest make call using .ajax() function of jQuery or using ajax extesion available in Asp.net MVC.

Pranay Rana
  • 175,020
  • 35
  • 237
  • 263
7

This works. But is this really async?

It is async in the fact that once you query your database (which is an IO bound operation), you free the ASP.NET Thread-Pool thread instead of using it to block until the query finishes.

Async doesn't mean "Return this request to the caller, and i'll finish executing at a later time", which is somewhat what you're expecting. It doesn't break the HTTP request-response protocol. What you want isn't achieved by async.

If you want the request to complete immediately, you'll need to queue it on some background thread, and push the data to the client side once the operation is complete.

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
2

Instead of async, use Task.Run:

Task.Run(() => MethodToExecuteInBackground(methodParameters)).ConfigureAwait(false).GetAwaiter();

You will also need to remove the async modifier from your controller Index method.

Now your controller method will return immediately while MethodToExecuteInBackground goes off and does some work in the background.

Be aware that this will tie up a thread-pool thread until MethodToExecuteInBackground finishes.

jjxtra
  • 20,415
  • 16
  • 100
  • 140