Using synchronous methods together with asynchrons

Asked

Viewed 430 times

7

For development reasons, I needed to create a call to an asynchronous method in a synchronous method, but when publishing my project on the server, it is indefinitely running.

Asynchronous method

public static async Task<IEnumerable<T>> QueryProfileAsync<T>(this DbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = default(int?), CommandType? commandType = default(CommandType?))
{
    if (HttpContext.Current.Request.IsLocal)
    {
        using (var profiled = new ProfiledDbConnection(cnn, MiniProfiler.Start()))
        {
            return await profiled.QueryAsync<T>(sql, param, transaction, commandTimeout, commandType);
        }
    }
    else return await cnn.QueryAsync<T>(sql, param, transaction, commandTimeout, commandType);
}

Synchronous method

public static IEnumerable<T> QueryProfile<T>(this DbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = default(int?), CommandType? commandType = default(CommandType?))
{
    return QueryProfileAsync<T>(cnn, sql, param, transaction, commandTimeout, commandType).GetAwaiter().GetResult();
}

In mode of debug by Visual Studio everything works perfectly. The solution was to take the call to the asynchronous method and perform the same operations, being as follows:

public static IEnumerable<T> QueryProfile<T>(this DbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = default(int?), CommandType? commandType = default(CommandType?))
{
    if (HttpContext.Current.Request.IsLocal)
    {
        using (var profiled = new ProfiledDbConnection(cnn, MiniProfiler.Start()))
        {
            return profiled.Query<T>(sql, param, transaction);
        }
    }
    else return cnn.Query<T>(sql, param, transaction);
}

The big question is: Is there a way to make a call to an asynchronous method in a synchronous method?

The need to have synchronous methods happens because it is not possible to make a call to a Action asynchronous using the Html.Action.

  • Are you using Dapper?

  • Yes, but I believe it is not Dapper, because I had the same problem in another similar case with an Entityframework Savechangesasync

  • It wasn’t even to know what you are using. I have whole project with Async and have no problem, so of course...

  • The big problem with me having to write non-asynchronous methods is that when I need one Html.Action does not work with Actions async http://stackoverflow.com/a/33915173/2221388

  • Um understood now, missed maybe say this in your question. It really doesn’t work. Only in the newest version ... kkkkai works

1 answer

4


I believe a deadlock is occurring, try to modify this your method QueryProfileAsync to use ConfigureAwait(false), thus:

public static async Task<IEnumerable<T>> QueryProfileAsync<T>(this DbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = default(int?), CommandType? commandType = default(CommandType?))
{
    if (HttpContext.Current.Request.IsLocal)
    {
        using (var profiled = new ProfiledDbConnection(cnn, MiniProfiler.Start()))
        {
            return await profiled.QueryAsync<T>(sql, param, transaction, commandTimeout, commandType).ConfigureAwait(false);
        }
    }
    else return await cnn.QueryAsync<T>(sql, param, transaction, commandTimeout, commandType).ConfigureAwait(false);
}

Whenever you use the await to await the execution of an asynchronous method within another asynchronous method, use the ConfigureAwait(false), unless you actually need to continue running the method within the synchronization context of the application you are running.

Deadlock can occur for the following reason:

  1. A synchronous method makes a synchronous call to an asynchronous method, thus blocking the synchronization context while waiting for the asynchronous method to end
  2. The asynchronous method makes a call to another asynchronous method with await (in your case the await profiled.QueryAsync<T>) without the ConfigureAwait(false)
  3. When the method profiled.QueryAsync<T> ends, the method QueryProfileAsync will attempt to continue its execution within the synchronization context (due to non-use of ConfigureAwait(false)), but it will not succeed because the synchronous method is with the same blocked.
  4. The asynchronous method will be locked until the synchronization context becomes available, but this will never happen, as the synchronous method will be indefinitely awaiting the completion of the asynchronous method.

I recommend reading this blog post by Stephen Cleary for a more detailed understanding of how all this works.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.