Qual é a assinatura certa para uma ação do controlador que retorna um IAsyncEnumerable<T>
e um NotFoundResult
mas ainda é processada de maneira assíncrona?
Eu usei essa assinatura e ela não é compilada porque IAsyncEnumerable<T>
não é possível esperar:
[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 compila bem, mas sua assinatura não é assíncrona. Então, eu estou preocupado se ele irá bloquear os threads do pool de threads ou não:
[HttpGet]
public IActionResult GetAll(Guid id)
{
try
{
return Ok(repository.GetAll(id)); // GetAll() returns an IAsyncEnumerable
}
catch (NotFoundException e)
{
return NotFound(e.Message);
}
}
Eu tentei usar um await foreach
loop assim, mas que obviamente também não seria compilado:
[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
Frederick The Fool
fonte
fonte
MyObject
itens com o mesmoid
? Normalmente, você não enviaria umNotFound
para algo que retorne umIEnumerable
- apenas estaria vazio - ou você retornaria o item único com oid
/ solicitadoNotFound
.IAsyncEnumerable
é aguardável. Useawait foreach(var item from ThatMethodAsync()){...}
.IAsyncEnumerable<MyObject>
, basta retornar o resultado, por exemploreturn objects
. Isso não converterá uma ação HTTP em um método gRPC ou SignalR de streaming. O middleware ainda consumirá os dados e enviará uma única resposta HTTP ao clienteIAsyncEnumerable
reconhecido como 3.0.Respostas:
A opção 2, que passa uma implementação de
IAsyncEnumerable<>
para aOk
chamada, está correta. O encanamento do ASP.NET Core cuida da enumeração e éIAsyncEnumerable<>
reconhecido como 3.0.Aqui está a chamada da pergunta, repetida para o contexto:
A chamada para
Ok
cria uma instância deOkObjectResult
, que herdaObjectResult
. O valor passado paraOk
é do tipoobject
, que é realizada naObjectResult
'sValue
propriedade. O ASP.NET Core MVC usa o padrão de comando , pelo qual o comando é uma implementaçãoIActionResult
e é executado usando uma implementação deIActionResultExecutor<T>
.For
ObjectResult
,ObjectResultExecutor
é usado para transformarObjectResult
em uma resposta HTTP. É a implementação doObjectResultExecutor.ExecuteAsync
que éIAsyncEnumerable<>
-aware:Como o código mostra, a
Value
propriedade é verificada para ver se é implementadaIAsyncEnumerable<>
(os detalhes estão ocultos na chamada paraTryGetReader
). Se sim,ExecuteAsyncEnumerable
é chamado, que executa a enumeração e passa o resultado enumerado paraExecuteAsyncCore
:reader
no trecho acima é onde ocorre a enumeração. Está enterrado um pouco, mas você pode ver a fonte aqui :O
IAsyncEnumerable<>
é enumerado em umaList<>
utilizaçãoawait foreach
, que, quase por definição, não bloqueia um encadeamento de solicitação. Como Panagiotis Kanavos chamou em um comentário sobre o OP, essa enumeração é realizada na íntegra antes de uma resposta ser enviada de volta ao cliente.fonte
Task
objeto. Esse fato em si dificulta a assincronicidade? Especialmente comparado a, digamos, um método semelhante que retornou aTask
.Task
, porque o próprio método não realiza nenhum trabalho assíncrono. É a enumeração assíncrona, que é tratada como descrito acima. Você pode ver queTask
é usado lá, na execução doObjectResult
.