web-dev-qa-db-fra.com

Comment gérer les opérations asynchrones dans Startup.Configure?

Dans mon application ASP.NET 5, je souhaite charger des données d'Azure dans un cache dans ma méthode Startup.Configure. Le kit de développement logiciel (SDK) Azure expose exclusivement les méthodes asynchrones. L'appel d'une méthode asynchrone se fait généralement par wait dans une méthode async, comme ceci:

public async Task Configure(IApplicationBuilder app, IMemoryCache cache)
{
    Data dataToCache = await DataSource.LoadDataAsync();
    cache.Set("somekey", dataToCache);

    // remainder of Configure method omitted for clarity
}

Cependant, ASP.NET 5 requiert que la méthode Configure retourne void. Je pourrais utiliser une méthode void async, mais je crois comprendre que les méthodes void async ne sont censées être utilisées que pour les gestionnaires d'événements (selon https://msdn.Microsoft.com/en-us/magazine/jj991977.aspx parmi beaucoup d'autres).

Je pensais qu'une meilleure façon de le faire serait d'appeler la fonction asynchrone sans attendre, d'appeler Wait sur la tâche renvoyée, puis de mettre en cache les résultats via la propriété Task.Results, comme ceci:

public void Configure(IApplicationBuilder app, IMemoryCache cache)
{
    Task<Data> loadDataTask = DataSource.LoadDataAsync();
    loadDataTask.Wait();
    cache.Set("somekey", loadDataTask.Result);

    // remainder of Configure method omitted for clarity
}

Stephen Walther a utilisé une approche similaire dans un blog post plus tôt cette année. Toutefois, ce poste n’indique pas clairement si cela est considéré comme une pratique acceptable. Est ce

Si cela est considéré comme une pratique acceptable, de quoi ai-je besoin, le cas échéant, du traitement des erreurs? Si j'ai bien compris, Task.Wait () relance toutes les exceptions émises par l'opération asynchrone et je n'ai pas fourni de mécanisme permettant d'annuler l'opération asynchrone. L'appel de Task.Wait () suffit-il?

52
DevHawk

L'exemple de code dans le blog auquel vous avez accédé n'utilisait que sync-over-async pour remplir une base de données avec des exemples de données; cet appel n'existerait pas dans une application de production.

Premièrement, je dirais que si vous avez vraiment besoin que Configure soit asynchrone, vous devez alors soulever un problème avec l'équipe ASP.NET afin que cela soit sur leur radar. Il ne serait pas trop difficile pour eux d’ajouter un support pour une ConfigureAsync à ce stade (c’est-à-dire avant la publication).

Deuxièmement, vous avez deux approches du problème. Vous pouvez utiliser task.Wait (ou, mieux encore, task.GetAwaiter().GetResult(), ce qui évite l'encapsuleur AggregateException si une erreur se produit). Ou bien, vous pouvez mettre en cache la tâche plutôt que le résultat de la tâche (ce qui fonctionne si IMemoryCache est davantage un dictionnaire qu'une sorte de série étrange sérialiser chose - je vous regarde, versions précédentes de ASP.NET).

Si cela est considéré comme une pratique acceptable, de quoi ai-je besoin, le cas échéant, du traitement des erreurs?

Utiliser GetAwaiter().GetResult() ferait propager l'exception (le cas échéant) hors de Configure. Je ne sais pas comment ASP.NET répondrait si configuration} _ l'application échouait cependant.

Je n'ai pas fourni de mécanisme pour annuler l'opération asynchrone.

Je ne sais pas comment vous pouvez "annuler" la configuration d'une application}, donc je ne m'inquiéterais pas pour cette partie-là.

29
Stephen Cleary

Vous pouvez effectuer un travail asynchrone, mais la méthode est synchrone et vous ne pouvez pas le changer. Cela signifie que vous devez attendre de manière synchronisée que les appels asynchrones soient terminés. 

Vous ne voulez pas revenir d'une méthode de démarrage si le démarrage n'est pas encore terminé, non? Votre solution semble aller bien. 

En ce qui concerne la gestion des exceptions: si votre application ne peut pas exécuter correctement une tâche, vous devez laisser la méthode de démarrage échouer (voir Fail-fast ). Si ce n'est pas quelque chose de critique, je voudrais inclure la partie pertinente dans un bloc catch catch et consigner simplement le problème pour une inspection ultérieure.

1
mbudnik

Les réponses ici ne fonctionnent pas toujours correctement si votre code asynchrone effectue d'autres appels asynchrones, en particulier s'il s'agit de rappels, vous pouvez alors trouver le blocage du code.

Cela m’est arrivé à de nombreuses reprises et j’ai utilisé le Nito.AsyncEx avec beaucoup d’effet.

using Nito.AsyncEx;

AsyncContext.Run(async () => { await myThing.DoAsyncTask(); });
0
Chris