web-dev-qa-db-fra.com

meilleure pratique d'utilisation d'Async dans l'API Web

J'ai l'API Web .NET core qui en tant que couche de service. La couche de service a tout le code EF.

Si vous avez un contrôleur de base avec ce code

protected Task<IActionResult> NewTask(Func<IActionResult> callback)
{
    return Task.Factory.StartNew(() =>
    {
        try
        {
            return callback();
        }
        catch (Exception ex)
        {
            Logger.LogError(ex.ToString());
            throw;
        }
    });
}

Dans l'action du contrôleur, j'encapsule tous les appels au service dans la méthode ci-dessus, par exemple :

[HttpGet("something")]
public async Task<IActionResult> GetSomething(int somethingId)
{
    return await NewTask(() =>
    {
        var result = _somethingService.GetSomething(somethingId);

        if (result != null)
            return Ok(result);
        else
            return NotFound("Role not found");
    });
}

Ce modèle est-il correct, compte tenu de demain, je peux avoir plusieurs appels de service en action ou passer des appels à un autre service Web. S'il vous plaît donnez votre avis.

10
krishna

je veux que mon API profite de l'async.

Non. L'exécution d'un travail synchrone sur le pool de threads vous offre les inconvénients du code asynchrone synchrone et , avec les avantages de ni l'un ni l'autre.

quelque chose de service a quelques opérations crud qui utilisent le noyau entityframework

Actuellement, votre méthode d'action est ce que j'appelle "faux asynchrone" - elle semble asynchrone (par exemple, en utilisant await), mais en fait, elle exécute simplement du code de blocage sur un thread d'arrière-plan. Sur ASP.NET, vous voulez une véritable asynchronie, whicn signifie que vous devez être asynchrone tout le long. Pour en savoir plus sur les raisons pour lesquelles cela est mauvais sur ASP.NET, consultez la première moitié de mon introduction à async sur l'article ASP.NET (il traite principalement des non-core ASP.NET, mais la première partie parlant des requêtes synchrones vs asynchrones est valable pour tout type de serveur).

Pour rendre cela vraiment asynchrone, vous devez commencer au niveau le plus bas - dans ce cas, vos appels EFCore. Ils prennent tous en charge l'asynchronie. Donc, remplacez les appels API comme x.FirstOrDefault() par await x.FirstOrDefaultAsync() (et la même chose pour toutes vos créations/mises à jour/suppressions, etc.).

Ensuite, laissez async/await se développer naturellement à partir de là; le compilateur vous guidera. Vous vous retrouverez avec des méthodes asynchrones sur votre somethingService qui peuvent être consommées comme telles:

[HttpGet("something")]
public async Task<IActionResult> GetSomething(int somethingId)
{
  var result = await _somethingService.GetSomethingAsync(somethingId);
  if (result != null)
    return Ok(result);
  else
    return NotFound("Role not found");
}
21
Stephen Cleary

D'accord, tout d'abord, vous devez cesser d'utiliser Task.Factory.StartNew et utilise Task.Run uniquement lorsque vous avez un gros travail lié au processeur que vous souhaitez exécuter sur un thread de pool de threads. Dans votre cas, vous n'en avez pas vraiment besoin. Vous devez également vous rappeler que vous ne devez utiliser que Task.Run lors de l'appel d'une méthode et non dans l'implémentation de la méthode. Vous pouvez en savoir plus à ce sujet ici .

Ce que vous voulez vraiment dans votre cas, c'est avoir un travail asynchrone dans votre service (je ne suis pas vraiment sûr que vous ayez même besoin d'un service dans votre cas) lorsque vous effectuez réellement un appel à la base de données et que vous souhaitez utiliser async/wait et pas seulement exécuter des trucs sur un fil d'arrière-plan.

Fondamentalement, votre service devrait ressembler à ceci (si vous êtes sûr d'avoir besoin d'un service):

class PeopleService
{
    public async Task<Person> GetPersonByIdAsync(int id)
    {
        Person randomPerson = await DataContext.People.FirstOrDefaultAsync(x => x.Id == id);
        return randomPerson;
    }
}

Comme vous pouvez le voir, votre service effectue maintenant des appels asynchrones vers la base de données et c'est essentiellement ce que devrait être votre modèle. Vous pouvez l'appliquer à toutes vos opérations (ajouter/supprimer/etc ..)

Après avoir rendu votre service asynchrone, vous devriez pouvoir facilement consommer les données de l'action.

Vos actions devraient ressembler à ceci:

[HttpGet("something")]
public async Task<IActionResult> GetPerson(int id)
{
    var result = await PeopleService.GetPersonByIdAsync(id);

    if (result != null)
        return Ok(result);
    else
        return NotFound("Role not found");
}
6
Kerim Emurla