Quelle est la bonne signature pour une action de contrôleur qui renvoie un IAsyncEnumerable<T>
et un NotFoundResult
mais qui est toujours traitée de manière asynchrone?
J'ai utilisé cette signature et elle ne se compile pas car elle IAsyncEnumerable<T>
n'est pas attendue:
[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);
}
}
Celui-ci compile bien mais sa signature n'est pas asynchrone. Je me demande donc si cela bloquera ou non les threads du pool de threads:
[HttpGet]
public IActionResult GetAll(Guid id)
{
try
{
return Ok(repository.GetAll(id)); // GetAll() returns an IAsyncEnumerable
}
catch (NotFoundException e)
{
return NotFound(e.Message);
}
}
J'ai essayé d'utiliser une await foreach
boucle comme celle-ci, mais cela ne se compilerait évidemment pas non plus:
[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
Frédéric le fou
la source
la source
MyObject
articles avec le mêmeid
? Normalement, vous n'enverriez pas unNotFound
pour quelque chose qui renvoie unIEnumerable
- il serait simplement vide - ou vous renverriez le seul élément avec leid
/ demandéNotFound
.IAsyncEnumerable
est attendue. Utilisezawait foreach(var item from ThatMethodAsync()){...}
.IAsyncEnumerable<MyObject>
, renvoyez simplement le résultat, par exemplereturn objects
. Ce ne sera pas convertir une action HTTP à une méthode GRPC ou SignalR le streaming bien. Le middleware consommera toujours les données et enverra une seule réponse HTTP au clientIAsyncEnumerable
consciente à partir de 3.0.Réponses:
L'option 2, qui transmet une implémentation de
IAsyncEnumerable<>
dans l'Ok
appel, est très bien. La plomberie ASP.NET Core prend en charge l'énumération et estIAsyncEnumerable<>
consciente à partir de 3.0.Voici l'appel de la question, répété pour le contexte:
L'appel à
Ok
crée une instance deOkObjectResult
, qui hériteObjectResult
. La valeur transmise pourOk
est du typeobject
, qui est maintenu dans leObjectResult
de »Value
la propriété. ASP.NET Core MVC utilise le modèle de commande , la commande étant une implémentation deIActionResult
et exécutée à l'aide d'une implémentation deIActionResultExecutor<T>
.Pour
ObjectResult
,ObjectResultExecutor
est utilisé pour transformer leObjectResult
en une réponse HTTP. C'est l' implémentation deObjectResultExecutor.ExecuteAsync
cela qui estIAsyncEnumerable<>
conscient:Comme le montre le code, la
Value
propriété est vérifiée pour voir si elle implémenteIAsyncEnumerable<>
(les détails sont masqués dans l'appel àTryGetReader
). Si c'est le cas,ExecuteAsyncEnumerable
est appelé, qui effectue l'énumération, puis transmet le résultat énuméré àExecuteAsyncCore
:reader
dans l'extrait ci-dessus est l'endroit où l'énumération se produit. C'est un peu enterré, mais vous pouvez voir la source ici :Le
IAsyncEnumerable<>
est énuméré dans uneList<>
utilisationawait foreach
qui, presque par définition, ne bloque pas un thread de demande. Comme Panagiotis Kanavos l'a indiqué dans un commentaire sur le PO, cette énumération est effectuée en entier avant qu'une réponse ne soit renvoyée au client.la source
Task
objet. Ce fait empêche-t-il en soi l'asynchronicité? Surtout par rapport à, disons, une méthode similaire qui a renvoyé aTask
.Task
, car la méthode elle-même n'effectue aucun travail asynchrone. C'est l'énumération asynchrone, qui est gérée comme décrit ci-dessus. Vous pouvez voir qu'ilTask
est utilisé ici, dans l'exécution duObjectResult
.