web-dev-qa-db-fra.com

Utiliser efficacement async / wait avec l’API Web ASP.NET

J'essaie d'utiliser la fonctionnalité async/await d'ASP.NET dans mon projet d'API Web. Je ne suis pas certain que cela améliorera les performances de mon service API Web. Veuillez trouver ci-dessous le flux de travail et l'exemple de code de mon application.

Flux de travail:

Application UI → Point de terminaison de l'API Web (contrôleur) → Méthode d'appel dans la couche de service API Web → Appelez un autre service Web externe. (Ici nous avons les interactions DB, etc.)

Contrôleur:

public async Task<IHttpActionResult> GetCountries()
{
    var allCountrys = await CountryDataService.ReturnAllCountries();

    if (allCountrys.Success)
    {
        return Ok(allCountrys.Domain);
    }

    return InternalServerError();
}

couche de service:

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
    var response = _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");

    return Task.FromResult(response);
}

J'ai testé le code ci-dessus et fonctionne. Mais je ne suis pas sûr qu'il s'agisse de l'utilisation correcte de async/await. S'il vous plaît partagez vos pensées.

98
arp

Je ne suis pas très sûr que cela va faire une différence dans les performances de mon API.

N'oubliez pas que l'avantage principal du code asynchrone côté serveur est évolutivité. Comme par magie, vos demandes ne seront pas exécutées plus rapidement. Je couvre plusieurs considérations "dois-je utiliser async" dans mon article sur async ASP.NET .

Je pense que votre cas d'utilisation (appel d'autres API) est bien adapté au code asynchrone, gardez simplement à l'esprit que "asynchrone" ne signifie pas "plus rapide". La meilleure approche consiste à rendre votre UI réactif et asynchrone; cela rendra votre application sentir plus rapide même si elle est légèrement plus lente.

En ce qui concerne le code, ce n'est pas asynchrone:

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
  var response = _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
  return Task.FromResult(response);
}

Une implémentation véritablement asynchrone est nécessaire pour tirer parti des avantages d'évolutivité de async:

public async Task<BackOfficeResponse<List<Country>>> ReturnAllCountriesAsync()
{
  return await _service.ProcessAsync<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
}

Ou (si votre logique dans cette méthode est vraiment juste une transmission):

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountriesAsync()
{
  return _service.ProcessAsync<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
}

Notez qu'il est plus facile de travailler du "dedans au dehors" plutôt que du "dehors dedans" comme ceci. En d'autres termes, ne commencez pas par une action de contrôleur asynchrone, puis forcez les méthodes en aval à être asynchrones. Identifiez plutôt les opérations naturellement asynchrones (appelez des API externes, des requêtes de base de données, etc.) et rendez-les asynchrones au niveau plus bas (Service.ProcessAsync). Laissez ensuite le async s'égoutter, rendant vos actions de contrôleur asynchrones à la dernière étape.

Et vous ne devez en aucun cas utiliser Task.Run dans ce scénario.

171
Stephen Cleary

C'est correct, mais peut-être pas utile.

Comme il n'y a rien à attendre - pas d'appels aux API de blocage pouvant fonctionner de manière asynchrone -, vous configurez des structures pour suivre les opérations asynchrones (ce qui entraîne une surcharge), sans pour autant utiliser cette fonctionnalité.

Par exemple, si la couche de service effectuait des opérations de base de données avec Entity Framework qui prend en charge les appels asynchrones:

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
    using (db = myDBContext.Get()) {
      var list = await db.Countries.Where(condition).ToListAsync();

       return list;
    }
}

Vous autoriseriez le thread de travail à faire autre chose alors que la base de données était interrogée (et donc capable de traiter une autre demande).

Attendre a tendance à être quelque chose qui doit aller jusqu'au bout: il est très difficile de réintégrer un système existant.

9
Richard

Vous n’exploitez pas efficacement async/wait car le thread de la demande sera bloqué lors de l’exécution de la méthode synchroneReturnAllCountries()

Le thread affecté à la gestion d'une demande attendra inutilement pendant que ReturnAllCountries() fera son travail.

Si vous pouvez implémenter ReturnAllCountries() pour qu'il soit asynchrone, vous bénéficierez donc d'avantages en termes d'évolutivité. En effet, le thread pourrait être libéré dans le pool de threads .NET pour gérer une autre demande, alors que ReturnAllCountries() était en cours d'exécution. Cela permettrait à votre service d’obtenir un débit plus élevé, en utilisant plus efficacement les threads.

0
James Wierzba