3

I am trying to make my base repository class asynchronous and I am running into some trouble. I am using Dapper ORM in my C# application.

Base method

protected async Task<List<T>> Read<T>(CommandDefinition cmd) {
    using(SqlConnection myCon = new SqlConnection(Config.DBConnection)) {
        await myCon.OpenAsync();

        IEnumerable<T> results = await myCon.QueryAsync<T>(cmd);

        List<T> retVal = results.ToList();

        myCon.Close();

        return retVal;
    }
}

Calling method

public List<Category> GetAllActiveCategories(Guid siteGuid) {
    return base.Read<Category>(SPNAME_GETALLACTIVE, siteGuid).Result;
}

Everything looks in order to me. I have the method declaration decorated with the async keyword. I am awaiting on asynchronous methods.

The problem that I am having is that the thread blocks on await myCon.OpenAsync();. This is my first attempt at using async and await, so I am sure that I am doing something wrong, but it is not obvious. Please help!

Nate Barbettini
  • 51,256
  • 26
  • 134
  • 147
fizch
  • 2,599
  • 3
  • 30
  • 45

2 Answers2

4

The posted Read code is fine. The issue is in the consuming code. It's common to get a deadlock with async if you call Wait() or Result on the returned Task or its antecedent Task up in the call chain.

As always in these cases, the general advice applies: don't block on async code. Once you start using async/await, you should be using async/await throughout your entire call chain.

So, your calling method becomes

public Task<List<Category>> GetAllActiveCategoriesAsync(Guid siteGuid) {
    return base.Read<Category>(SPNAME_GETALLACTIVE, siteGuid);
}

... or

public async Task<List<Category>> GetAllActiveCategoriesAsync(Guid siteGuid) {
    List<Category> result = await base.Read<Category>(SPNAME_GETALLACTIVE, siteGuid);

    // Do something.

    return result;
}
Kirill Shlenskiy
  • 9,367
  • 27
  • 39
  • my only question becomes how do I access the results at the top most level without blocking it – fizch Jun 16 '16 at 04:32
  • 1
    @fizch, unfortunately, async has a way of "poisoning" your code - in that you have to rewrite the *entire* chain to be `async`. Of course, if you're working with a synchronous calling chain you can always use the old-school blocking methods in your particular case. Alternatively you can fix your problem by using `ConfigureAwait(false)` on all `Task`s awaited inside your `Read` method, but, while it is a best practice to do so, intentionally blocking on `Task`s is still an antipattern and should be avoided. – Kirill Shlenskiy Jun 16 '16 at 04:42
  • But I digress as I am no longer answering the original question as stated. – Kirill Shlenskiy Jun 16 '16 at 04:44
1

The culprit is:

return base.Read<Category>(SPNAME_GETALLACTIVE, siteGuid).Result;

As Kirill noted, any time you use .Wait() or .Result on a Task, you are blocking synchronously. What you need to do is this:

public Task<List<Category>> GetAllActiveCategories(Guid siteGuid) {
    return base.Read<Category>(SPNAME_GETALLACTIVE, siteGuid);
}

This will return a Task to the calling method of this method, and so on... it has to be async "all the way up".

If the top-level consumer of this code is ASP.NET, you're fine. Just return a Task<IActionResult> (or the appropriate return type wrapped in a Task) and the framework will sort out the awaiting for you.

If you're writing a console application or otherwise can't make it "async all the way up", you'll have to either block on .Result or make your method async void and use await. Neither one is a great solution, sadly. Async/await is pretty aggressive in the sense that you really do have to use it throughout your stack.

Nate Barbettini
  • 51,256
  • 26
  • 134
  • 147