web-dev-qa-db-fra.com

Entity Framework SaveChanges () contre SaveChangesAsync () et Find () contre FindAsync ()

Je cherchais les différences entre 2 paires ci-dessus, mais je n’ai trouvé aucun article expliquant clairement à ce sujet ni sur le moment d’utiliser l’un ou l’autre.

Alors, quelle est la différence entre SaveChanges() et SaveChangesAsync()?
Et entre Find() et FindAsync()?

Côté serveur, lorsque nous utilisons les méthodes Async, nous devons également ajouter await. Ainsi, je ne pense pas que ce soit asynchrone côté serveur.

Cela aide-t-il uniquement à empêcher le blocage de l'interface utilisateur sur le navigateur côté client? Ou y a-t-il des avantages et des inconvénients entre eux?

70
Hien Tran

Chaque fois que vous devez effectuer une action sur un serveur distant, votre programme génère la demande, l'envoie puis attend une réponse. Je vais utiliser SaveChanges() et SaveChangesAsync() comme exemple, mais il en va de même pour Find() et FindAsync().

Supposons que vous avez une liste myList de plus de 100 éléments à ajouter à votre base de données. Pour insérer cela, votre fonction ressemblerait à quelque chose comme ceci:

using(var context = new MyEDM())
{
    context.MyTable.AddRange(myList);
    context.SaveChanges();
}

Tout d'abord, vous créez une instance de MyEDM, ajoutez la liste myList à la table MyTable, puis appelez SaveChanges() pour conserver les modifications apportées à la base de données. Cela fonctionne comme vous le souhaitez, les enregistrements sont validés, mais votre programme ne peut rien faire d’autre avant la fin de la validation. Cela peut prendre beaucoup de temps, selon ce que vous commettez. Si vous validez des modifications dans les enregistrements, l'entité doit les valider une à la fois (j'ai déjà eu une sauvegarde, prenez 2 minutes pour les mises à jour)!

Pour résoudre ce problème, vous pouvez faire l'une des deux choses suivantes. La première est que vous pouvez démarrer un nouveau thread pour gérer l'insertion. Bien que cela libère le thread appelant pour qu'il puisse continuer à s'exécuter, vous avez créé un nouveau thread qui va simplement rester là et attendre. Cette surcharge n'est pas nécessaire, et c'est ce que résout le modèle async await.

Pour les opérations d'E/S, await devient rapidement votre meilleur ami. En prenant la section de code ci-dessus, nous pouvons la modifier pour être:

using(var context = new MyEDM())
{
    Console.WriteLine("Save Starting");
    context.MyTable.AddRange(myList);
    await context.SaveChangesAsync();
    Console.WriteLine("Save Complete");
}

C'est un très petit changement, mais il y a de profonds effets sur l'efficacité et les performances de votre code. Alors qu'est-ce qui se passe? Le début du code est identique, vous créez une instance de MyEDM et ajoutez votre myList à MyTable. Mais lorsque vous appelez await context.SaveChangesAsync(), l'exécution du code retourne à la fonction appelante! Ainsi, pendant que vous attendez que tous ces enregistrements soient validés, votre code peut continuer à s'exécuter. Disons que la fonction qui contenait le code ci-dessus avait la signature de public async Task SaveRecords(List<MyTable> saveList), la fonction appelante pourrait ressembler à ceci:

public async Task MyCallingFunction()
{
    Console.WriteLine("Function Starting");
    Task saveTask = SaveRecords(GenerateNewRecords());

    for(int i = 0; i < 1000; i++){
        Console.WriteLine("Continuing to execute!");
    }

    await saveTask;
    Console.Log("Function Complete");
}

Pourquoi vous auriez une fonction comme celle-ci, je ne sais pas, mais ce qu’elle affiche montre comment fonctionne async await. Tout d'abord, passons en revue ce qui se passe.

L'exécution entre MyCallingFunction, Function Starting, Puis Save Starting Est écrite sur la console, puis la fonction SaveChangesAsync() est appelée. A ce stade, l'exécution retourne à MyCallingFunction et entre dans la boucle for en écrivant 'Continuer à exécuter' jusqu'à 1000 fois. Lorsque SaveChangesAsync() a terminé, l'exécution revient à la fonction SaveRecords, en écrivant Save Complete Sur la console. Une fois que tout est terminé dans SaveRecords, l'exécution se poursuivra dans MyCallingFunction à droite si c'était quand SaveChangesAsync() a terminé. Confus? Voici un exemple de sortie:

 Fonction de démarrage 
 Sauvegarder Démarrage de 
 Continuer à exécuter! 
 Continuer à exécuter! 
 Continuer à exécuter! 
 Continuer à exécuter! 
 Continuer à exécuter! 
 .... 
 Continuer à exécuter! 
 Sauvegarder Terminé! 
 Continuer à exécuter! 
 Continuer pour exécuter! 
 Continuer à exécuter! 
 .... 
 Continuer à exécuter! 
 Fonction terminée! 

Ou peut-être:

 Fonction de démarrage 
 Sauvegarder Démarrer de 
 Continuer à exécuter! 
 Continuer à exécuter! 
 Sauvegarder Terminé! 
 Continuer à exécuter! .____.] Continuer à exécuter! 
 Continuer à exécuter! 
 .... 
 Continuer à exécuter! 
 Fonction terminée! 

C'est la beauté de async await, Votre code peut continuer à s'exécuter pendant que vous attendez que quelque chose se termine. En réalité, vous auriez une fonction plus semblable à celle-ci en tant que fonction d'appel:

public async Task MyCallingFunction()
{
    List<Task> myTasks = new List<Task>();
    myTasks.Add(SaveRecords(GenerateNewRecords()));
    myTasks.Add(SaveRecords2(GenerateNewRecords2()));
    myTasks.Add(SaveRecords3(GenerateNewRecords3()));
    myTasks.Add(SaveRecords4(GenerateNewRecords4()));

    await Task.WhenAll(myTasks.ToArray());
}

Ici, vous avez quatre fonctions de sauvegarde différentes en même temps. MyCallingFunction se terminera beaucoup plus rapidement avec async await que si les fonctions SaveRecords individuelles étaient appelées en série.

La seule chose que je n’ai pas encore évoquée est le mot-clé await. Cela empêche la fonction en cours de s'exécuter jusqu'à ce que Task que vous attendiez soit terminé. Ainsi, dans le cas du MyCallingFunction original, la ligne Function Complete Ne sera pas écrite sur la console tant que la fonction SaveRecords ne sera pas terminée.

Bref, si vous avez la possibilité d’utiliser async await, Vous devriez augmenter les performances de votre application.

144
Jacob Lambert

Cette déclaration est incorrecte:

Côté serveur, lorsque nous utilisons des méthodes Async, nous devons également ajouter wait.

Vous n'avez pas besoin d'ajouter "wait". "wait" est simplement un mot-clé pratique en C # qui vous permet d'écrire plus de lignes de code après l'appel, et ces autres lignes ne seront exécutées qu'une fois l'opération de sauvegarde terminée. Mais comme vous l'avez fait remarquer, vous pouvez y parvenir simplement en appelant SaveChanges au lieu de SaveChangesAsync.

Mais fondamentalement, un appel asynchrone est beaucoup plus que cela. L'idée ici est que si vous pouvez effectuer d'autres tâches (sur le serveur) pendant que l'opération de sauvegarde est en cours, vous devez utiliser SaveChangesAsync. Ne pas utiliser "wait". Appelez simplement SaveChangesAsync, puis continuez à faire d’autres tâches en parallèle. Cela inclut potentiellement, dans une application Web, le renvoi d'une réponse au client même avant la fin de la sauvegarde. Mais bien sûr, vous voudrez toujours vérifier le résultat final de la sauvegarde afin qu'en cas d'échec, vous puissiez le communiquer à votre utilisateur ou le connecter d'une manière ou d'une autre.

1
Rajeev Goel