Estoy tratando de llamar a un método asíncrono desde un método sincrónico y sigue bombardeando la llamada a GetUsTraceApiHealth() pero sin errores. ¿Cuál es el problema?

Método de llamada:

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);
    }
}

Método que se llama:

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);
        }
    }
}
0
Dean Friedland 12 ene. 2018 a las 22:19

3 respuestas

La mejor respuesta

Siga el mantra asíncrono de "asíncrono hasta el final". Básicamente, debería casi nunca llamar a .Result en una tarea. En la mayoría de los casos, su método de llamada también debe ser asíncrono. Entonces simplemente puede esperar el resultado de la operación:

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

Debería corresponder al host de la aplicación en el nivel superior (en este caso, ASP.NET y el servidor web) para manejar el contexto de sincronización. No debe intentar enmascarar una operación asincrónica como una sincrónica.

5
David 12 ene. 2018 a las 19:31

Versión simplificada de su código:

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);
    }
}

No hay ninguna razón para devolver el HttpResponseMessage para leer su contenido como string, solo use GetStringAsync.
Además, nunca catch sea una excepción solo para volver a lanzarlo. Si necesita hacer eso, use:

catch(Exception ex)
{
    //log or whatever
    throw;
}
3
Camilo Terevinto 12 ene. 2018 a las 19:29

No debe mezclar las operaciones async y sync juntas. La forma correcta de realizarlo es decorando sus métodos como async y simplemente usando 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);
    }
}

Si no puede aplicar async en todas las rutas, puede usar ConfigureAwait para evitar un punto muerto.

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);
        }
    }
}
0
lucky 12 ene. 2018 a las 20:44