6

So I've run into a little issue that puzzles me and I've not been able to find a good explanation for this - I imagine I'm probably mis-using the async/await feature somehow but I really don't know what I'm doing wrong.

So I have some sql code that queries my database and returns a single value. I was therefore using ExecuteScalarAsync to get that value out into c#.

The code is as follows:

public void CheckOldTransactionsSync()
{
    CheckOldTransactions().Wait();
}

public async Task CheckOldTransactions()
{
    DateTimeOffset beforeThis = DateTime.UtcNow.Subtract(TimeSpan.FromHours(6));

    using (SqlConnection connection = new SqlConnection(SqlConnectionString))
    {
        await connection.OpenAsync(cts.Token);
        using (SqlCommand command = new SqlCommand(@"SELECT TOP 1 1 AS value FROM SyncLog WHERE [TimeStamp] < @BeforeThis;", connection))
        {
            command.Parameters.Add("@BeforeThis", System.Data.SqlDbType.DateTimeOffset, 7);

            command.Prepare();
            command.Parameters["@BeforeThis"].Value = beforeThis;

            Int32 oldTransactions = (Int32)await command.ExecuteScalarAsync(cts.Token);

            // do stuff with oldTransactions
        }
    }
}

So elsewhere in my code the CancellationTokenSource called cts is created and set to expire after 2 minutes using the CancelAfter method.

Now I've stepped through this code with the debugger and I reach the line where I await the call to ExecuteScalarAsync without a problem. However I seem to have two issues with the execution of that line which are that it doesn't seem to return and 2 it ignores my cancellation token and is still running some time after my two minute cancellation token has expired.

Now I've run the sql query in Sql Studio and it returns very quickly - the table has only around 4000 rows at this time.

I've resolved the problem for now by changing that line to:

Int32 oldTransactions = (Int32) command.ExecuteScalar();

Which returns almost instantaneously.

That is the only line of code I've changed and I changed it back just to make sure and the same issue occurred. So my question is, what did I do wrong with the asynchronous call?

m3z
  • 980
  • 16
  • 32
  • 1
    Are you calling `Task.Wait` or `Task.Result` further up your call stack? If so, you are probably seeing a [deadlock](http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html) that I explain on my blog. – Stephen Cleary Jun 24 '15 at 13:21
  • Could you please add the code showing how you call `CheckOldTransactions()` and how you wait for it's completion? – Nitram Jun 24 '15 at 14:15
  • Hi Stephen, yes i do call it with a .Wait() in this case - I will read your blog and see if that can help. Thanks – m3z Jun 24 '15 at 14:59
  • @StephenCleary you said it - thats exactly what I needed to read. Thanks – m3z Jun 24 '15 at 15:08

1 Answers1

5

You are calling Wait.

That's a classic ASP.NET deadlock. Don't block, or use synchronous IO.

Community
  • 1
  • 1
usr
  • 168,620
  • 35
  • 240
  • 369