Home C# Async/Await call not working
Reply: 3

C# Async/Await call not working

Dean Friedland
1#
Dean Friedland Published in 2018-01-12 19:19:22Z

I am trying to call an async method from a synchronous method and it keeps bombing on the call to GetUsTraceApiHealth() but with no errors. What is the problem?

Calling Method:

public ActionResult TestSSN()
{
    try
    {
        var apiResponse = GetUsTraceApiHealth().GetAwaiter().GetResult();
        string responseBody = apiResponse.Content.ReadAsStringAsync().Result;
        return Json(responseBody, JsonRequestBehavior.AllowGet);
    }
    catch (Exception e)
    {                    
        throw new Exception(e.Message);
    }
}

Method Being Called:

public async Task<HttpResponseMessage> GetUsTraceApiHealth()
{
    using (HttpClient httpClient = new HttpClient())
    {
        try
        {
            string uri = $"https://trace.{ConfigHelper.SterlingDomain}health?deep";

            HttpResponseMessage apiResponse = await httpClient.GetAsync(uri);
            return apiResponse;
        }
        catch (Exception e)
        {
            throw new Exception(e.Message);
        }
    }
}
David
2#
David Reply to 2018-01-12 19:31:44Z

Follow the async mantra of "async all the way down". Basically, you should almost never call .Result on a task. In the majority of cases, your calling method should also be async. Then you can simply await the result of the operation:

public async Task<ActionResult> TestSSN()
{
    //...
    var apiResponse = await GetUsTraceApiHealth();
    string responseBody = await apiResponse.Content.ReadAsStringAsync();
    //...
}

It should be up to the application host at the top level (in this case ASP.NET and the web server) to handle the synchronization context. You shouldn't try to mask an asynchronous operation as a synchronous one.

Camilo Terevinto
3#
Camilo Terevinto Reply to 2018-01-12 19:29:04Z

Simplified version of your code:

public async Task<ActionResult> TestSSN()
{
    var apiResponse = await GetUsTraceApiHealthAsync();
    return Json(apiResponse, JsonRequestBehavior.AllowGet);
}

public async Task<string> GetUsTraceApiHealthAsync()
{
    using (HttpClient httpClient = new HttpClient())
    {
        string uri = $"https://trace.{ConfigHelper.SterlingDomain}health?deep";

        return apiResponse = await httpClient.GetStringAsync(uri);
    }
}

There's no reason to return the HttpResponseMessage to read its content as string, just use GetStringAsync.
Also, never catch an exception just to rethrow it. If you need to do that, use:

catch(Exception ex)
{
    //log or whatever
    throw;
}
Stormcloak
4#
Stormcloak Reply to 2018-01-12 20:44:34Z

You shouldn't mix the async and sync operations together. Proper way to perform it is decorating your methods as async and simply using await;

public async Task<ActionResult> TestSSN()
{
    try
    {
        var apiResponse = await GetUsTraceApiHealth().GetAwaiter().GetResult();
        string responseBody = await apiResponse.Content.ReadAsStringAsync().Result;
        return Json(responseBody, JsonRequestBehavior.AllowGet);
    }
    catch (Exception e)
    {                    
        throw new Exception(e.Message);
    }
}

If you don't able to apply async in the all paths, you could use ConfigureAwait to prevent deadlock.

public async Task<HttpResponseMessage> GetUsTraceApiHealth()
{
    using (HttpClient httpClient = new HttpClient())
    {
        try
        {
            string uri = $"https://trace.{ConfigHelper.SterlingDomain}health?deep";

            HttpResponseMessage apiResponse = await httpClient.GetAsync(uri).ConfigureAwait(false);
            return apiResponse;
        }
        catch (Exception e)
        {
            throw new Exception(e.Message);
        }
    }
}
You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.301632 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO