c# - iactionresult - la mejor práctica para usar async aguarde en webapi
web api async controller (2)
Tengo .NET core Web API que como capa de servicio. La capa de servicio tiene todo el código EF.
Si tiene basecontroller con este código
protected Task<IActionResult> NewTask(Func<IActionResult> callback)
{
return Task.Factory.StartNew(() =>
{
try
{
return callback();
}
catch (Exception ex)
{
Logger.LogError(ex.ToString());
throw;
}
});
}
En la acción del controlador envuelvo todas las llamadas al servicio en el método anterior, por ejemplo:
[HttpGet("something")]
public async Task<IActionResult> GetSomething(int somethingId)
{
return await NewTask(() =>
{
var result = _somethingService.GetSomething(somethingId);
if (result != null)
return Ok(result);
else
return NotFound("Role not found");
});
}
Si este patrón correcto se considera mañana, es posible que tenga más de un servicio de llamadas en acción o realice llamadas a otro servicio web. Por favor avise.
Quiero que mi api se beneficie de la función asíncrona. El patrón anterior servirá para estas necesidades
No, no lo hace. Ejecutar trabajo sincrónico en el grupo de subprocesos le ofrece los inconvenientes del código síncrono y asíncrono, con los beneficios de ninguno.
algo el servicio tiene algunas operaciones crud que utilizan entityframework core
Actualmente, su método de acción es lo que llamo "falso asíncrono": parece asíncrono (por ejemplo, usando await
), pero de hecho solo está ejecutando código de bloqueo en una cadena de fondo. En ASP.NET, quiere una verdadera asincronía, lo que significa que debe ser asincrónico todo el camino. Para obtener más información sobre por qué esto es malo en ASP.NET, consulte la primera mitad de mi introducción a la async
en el artículo de ASP.NET (en su mayoría se trata de ASP.NET no núcleo, pero la primera parte que habla de solicitudes síncronas vs asincrónicas es válida para cualquier tipo de servidor).
Para que esto sea verdaderamente asincrónico, debe comenzar en el nivel más bajo, en este caso, sus llamadas EFCore. Todos ellos admiten asincronía. Por lo tanto, reemplace las llamadas API como x.FirstOrDefault()
con await x.FirstOrDefaultAsync()
(y lo mismo para todas sus creaciones / actualizaciones / eliminaciones, etc.).
Luego permita que async
/ await
crecer naturalmente desde allí; el compilador te guiará Terminará con métodos asíncronos en su somethingService
que se pueden consumir como tales:
[HttpGet("something")]
public async Task<IActionResult> GetSomething(int somethingId)
{
var result = await _somethingService.GetSomethingAsync(somethingId);
if (result != null)
return Ok(result);
else
return NotFound("Role not found");
}
De acuerdo, antes que nada, debe dejar de usar Task.Factory.StartNew
y usar Task.Run
solo cuando tenga trabajos pesados vinculados a la CPU que desee ejecutar en una cadena de subprocesos. En tu caso, realmente no necesitas eso en absoluto. También debe recordar que solo debe usar Task.Run
cuando llame a un método y no en la implementación del método. Puedes leer más sobre eso aquí .
Lo que realmente quiere en su caso es tener un trabajo asincrónico dentro de su servicio (no estoy realmente seguro de que necesite siquiera un servicio en su caso) cuando en realidad está haciendo una llamada a la base de datos y desea usar async / await y no solo ejecutar algunas cosas en un hilo de fondo.
Básicamente, su servicio debería verse de la siguiente manera (si está seguro de que necesita un servicio):
class PeopleService
{
public async Task<Person> GetPersonByIdAsync(int id)
{
Person randomPerson = await DataContext.People.FirstOrDefaultAsync(x => x.Id == id);
return randomPerson;
}
}
Como puede ver, su servicio ahora realiza llamadas asincrónicas a la base de datos y eso es básicamente lo que su patrón debería ser. Puede aplicar esto a todas sus operaciones (agregar / eliminar / etc.)
Después de hacer que su servicio sea asincrónico, podrá fácilmente consumir los datos en la acción.
Tus acciones deberían verse más o menos así:
[HttpGet("something")]
public async Task<IActionResult> GetPerson(int id)
{
var result = await PeopleService.GetPersonByIdAsync(id);
if (result != null)
return Ok(result);
else
return NotFound("Role not found");
}