Estoy tratando de hacer uso de la función async/await de ASP.NET en mi proyecto de API web. No estoy muy seguro de si supondrá alguna diferencia en el rendimiento de mi servicio Web API. A continuación encontrará el flujo de trabajo y el código de muestra de mi aplicación.
Flujo de trabajo:
Aplicación de interfaz de usuario → Punto final de API web (controlador) → Método de llamada en la capa de servicio de API web → Llamar a otro servicio web externo. (Aquí tenemos las interacciones DB, etc.)
Controlador:
public async Task<IHttpActionResult> GetCountries() { var allCountrys = await CountryDataService.ReturnAllCountries(); if (allCountrys.Success) { return Ok(allCountrys.Domain); } return InternalServerError(); }Capa de servicio:
public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries() { var response = _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries"); return Task.FromResult(response); } Probé el código anterior y está funcionando. Pero no estoy seguro de si es el uso correcto de async/await . Por favor, comparta sus pensamientos.
No estoy muy seguro de si hará alguna diferencia en el rendimiento de mi API.
Tenga en cuenta que el principal beneficio del código asincrónico en el lado del servidor es la escalabilidad . No hará que sus solicitudes se ejecuten mágicamente más rápido. Cubro varias consideraciones de "debería usar async " en mi artículo sobre async ASP.NET .
Creo que su caso de uso (llamar a otras API) es adecuado para el código asincrónico, solo tenga en cuenta que "asincrónico" no significa "más rápido". El mejor enfoque es primero hacer que su interfaz de usuario sea receptiva y asíncrona; esto hará que su aplicación se sienta más rápida incluso si es un poco más lenta.
En lo que respecta al código, esto no es asíncrono:
public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries() { var response = _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries"); return Task.FromResult(response); } Necesitaría una implementación verdaderamente asíncrona para obtener los beneficios de escalabilidad de async :
public async Task<BackOfficeResponse<List<Country>>> ReturnAllCountriesAsync() { return await _service.ProcessAsync<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries"); }O (si su lógica en este método realmente es solo un paso):
public Task<BackOfficeResponse<List<Country>>> ReturnAllCountriesAsync() { return _service.ProcessAsync<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries"); } Tenga en cuenta que es más fácil trabajar de "adentro hacia afuera" en lugar de "afuera hacia adentro" de esta manera. En otras palabras, no comience con una acción de controlador asíncrona y luego obligue a los métodos posteriores a ser asíncronos. En su lugar, identifique las operaciones asincrónicas naturales (llamadas a API externas, consultas de bases de datos, etc.) y hágalas asincrónicas primero en el nivel más bajo ( Service.ProcessAsync ). Luego, deje que la async gradualmente, haciendo que las acciones de su controlador sean asincrónicas como último paso.
Y bajo ninguna circunstancia debe usar Task.Run en este escenario.
Es correcto, pero quizás no útil.
Como no hay nada que esperar, no hay llamadas para bloquear las API que podrían operar de forma asíncrona, entonces está configurando estructuras para rastrear la operación asíncrona (que tiene una sobrecarga) pero luego no hace uso de esa capacidad.
Por ejemplo, si la capa de servicio estaba realizando operaciones de base de datos con Entity Framework, que admite llamadas asíncronas:
public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries() { using (db = myDBContext.Get()) { var list = await db.Countries.Where(condition).ToListAsync(); return list; } }Permitiría que el subproceso de trabajo hiciera otra cosa mientras se consultaba la base de datos (y, por lo tanto, podía procesar otra solicitud).
Await tiende a ser algo que necesita ir hasta el final: es muy difícil adaptarlo a un sistema existente.
No está aprovechando async/await de manera efectiva porque el subproceso de solicitud se bloqueará mientras se ejecuta el método síncrono ReturnAllCountries()
El subproceso que se asigna para manejar una solicitud estará esperando ociosamente mientras ReturnAllCountries() hace su trabajo.
Si puede implementar ReturnAllCountries() para que sea asíncrono, verá beneficios de escalabilidad. Esto se debe a que el subproceso podría devolverse al grupo de subprocesos de .NET para manejar otra solicitud, mientras se ejecuta ReturnAllCountries() . Esto permitiría que su servicio tenga un mayor rendimiento al utilizar subprocesos de manera más eficiente.