¿Cuál es la firma correcta para una acción de controlador que devuelve un IAsyncEnumerable<T>
y a NotFoundResult
pero que aún se procesa de forma asíncrona?
IAsyncEnumerable<T>
Usé esta firma y no se compila porque no está a la espera:
[HttpGet]
public async Task<IActionResult> GetAll(Guid id)
{
try
{
return Ok(await repository.GetAll(id)); // GetAll() returns an IAsyncEnumerable
}
catch (NotFoundException e)
{
return NotFound(e.Message);
}
}
Este se compila bien pero su firma no es asíncrona. Entonces me preocupa si bloqueará los hilos del grupo de hilos o no:
[HttpGet]
public IActionResult GetAll(Guid id)
{
try
{
return Ok(repository.GetAll(id)); // GetAll() returns an IAsyncEnumerable
}
catch (NotFoundException e)
{
return NotFound(e.Message);
}
}
Intenté usar un await foreach
bucle como este, pero eso obviamente tampoco compilaría:
[HttpGet]
public async IAsyncEnumerable<MyObject> GetAll(Guid id)
{
IAsyncEnumerable<MyObject> objects;
try
{
objects = contentDeliveryManagementService.GetAll(id); // GetAll() returns an IAsyncEnumerable
}
catch (DeviceNotFoundException e)
{
return NotFound(e.Message);
}
await foreach (var obj in objects)
{
yield return obj;
}
}
c#
asp.net-core
async-await
asp.net-core-mvc
Federico el tonto
fuente
fuente
MyObject
artículos con lo mismoid
? Normalmente, no enviaría unNotFound
por algo que devuelve unIEnumerable
, simplemente estaría vacío, o devolvería el artículo individual con elid
/ solicitadoNotFound
.IAsyncEnumerable
Está a la espera. Usoawait foreach(var item from ThatMethodAsync()){...}
.IAsyncEnumerable<MyObject>
, simplemente devuelva el resultado, por ejemploreturn objects
. Sin embargo, eso no convertirá una acción HTTP en un método gRPC o SignalR de transmisión. El middleware seguirá consumiendo los datos y enviará una única respuesta HTTP al clienteIAsyncEnumerable
conocimiento de 3.0.Respuestas:
La opción 2, que pasa una implementación
IAsyncEnumerable<>
a laOk
llamada, está bien. La fontanería ASP.NET Core se encarga de la enumeración y tieneIAsyncEnumerable<>
conocimiento de 3.0.Aquí está la llamada de la pregunta, repetida para el contexto:
La llamada a
Ok
crea una instancia deOkObjectResult
, que heredaObjectResult
. El valor pasado aOk
es de tipoobject
, que se celebra en elObjectResult
'sValue
propiedad. ASP.NET Core MVC usa el patrón de comando , mediante el cual el comando es una implementación deIActionResult
y se ejecuta usando una implementación deIActionResultExecutor<T>
.Para
ObjectResult
,ObjectResultExecutor
se utiliza para convertir elObjectResult
en una respuesta HTTP. Es la implementación deObjectResultExecutor.ExecuteAsync
eso esIAsyncEnumerable<>
-aware:Como muestra el código, la
Value
propiedad se verifica para ver si se implementaIAsyncEnumerable<>
(los detalles están ocultos en la llamada aTryGetReader
). Si lo hace,ExecuteAsyncEnumerable
se llama, que realiza la enumeración y luego pasa el resultado enumerado aExecuteAsyncCore
:reader
en el fragmento anterior es donde ocurre la enumeración. Está enterrado un poco, pero puedes ver la fuente aquí :El
IAsyncEnumerable<>
se enumera en unList<>
usoawait foreach
, que, casi por definición, no bloquea un hilo de solicitud. Como Panagiotis Kanavos llamó en un comentario sobre el OP, esta enumeración se realiza en su totalidad antes de que se envíe una respuesta al cliente.fuente
Task
objeto. ¿Ese hecho en sí mismo obstaculiza la asincronía de alguna manera? Especialmente comparado con, digamos, un método similar que devolvió aTask
.Task
, porque el método en sí no realiza ningún trabajo asincrónico. La enumeración es asincrónica, que se maneja como se describe anteriormente. Puede ver queTask
se usa allí, en la ejecución deObjectResult
.