Entity Framework SaveChanges() vs. SaveChangesAsync () et Find () vs. FindAsync()
J'ai cherché les différences entre les paires 2 ci-dessus mais je n'ai trouvé aucun article expliquant clairement à ce sujet ainsi que quand utiliser l'un ou l'autre.
Quelle est Donc la différence entre SaveChanges()
et SaveChangesAsync()
?
Et entre Find()
et FindAsync()
?
Côté serveur, lorsque nous utilisons des méthodes Async
, nous devons également ajouter await
. Ainsi, Je ne pense pas que ce soit asynchrone côté serveur.
Cela aide-t-il seulement à empêcher le blocage de l'interface utilisateur sur le navigateur côté client? Ou il y a des avantages et des inconvénients entre eux?
2 réponses
Chaque fois que vous devez effectuer une action sur un serveur distant, votre programme génère la requête, l'envoie, puis attend une réponse. Je vais utiliser SaveChanges()
et SaveChangesAsync()
, comme un exemple, mais le même s'applique à Find()
et FindAsync()
.
Disons que vous avez une liste myList
de plus de 100 éléments que vous devez ajouter à votre base de données. Pour insérer cela, votre fonction ressemblerait à ceci:
using(var context = new MyEDM())
{
context.MyTable.AddRange(myList);
context.SaveChanges();
}
Vous créez D'abord 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 engagés, mais votre programme ne peut rien faire d'autre tant que la validation n'est pas terminée. Cela peut prendre beaucoup de temps en fonction de ce que vous commettez. Si vous commettez des modifications aux enregistrements, entity doit les valider une à la fois (j'ai déjà eu une sauvegarde prendre 2 minutes pour les mises à jour)!
Pour résoudre ce problème, vous pouvez faire une des deux choses. Le premier est que vous pouvez démarrer un nouveau thread pour gérer l'insertion. Tout ce va libérer le thread appelant pour continuer à s'exécuter, vous avez créé un nouveau thread qui va juste rester là et attendre. Il n'y a pas besoin de ce surcoût, et c'est ce que le modèle async await
résout.
Pour les oppé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 des effets profonds sur l'efficacité et la performance de votre code. Donc ce qui se passe? Le début de l' le code est le même, vous créez une instance de MyEDM
et ajouter votre myList
à MyTable
. Mais lorsque vous appelez await context.SaveChangesAsync()
, l'exécution du code retourne à la fonction d'appel!{[56] } 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 produit montre comment async await
Fonctionne. Tout d'abord laisse aller sur ce qui se passe.
L'Exécution saisit MyCallingFunction
, Function Starting
alors Save Starting
est écrit sur la console, alors la fonction SaveChangesAsync()
est appelée. À ce stade, l'exécution retourne à MyCallingFunction
et entre dans la boucle for en écrivant 'continuer à exécuter' jusqu'à 1000 fois. Lorsque SaveChangesAsync()
finalise, l'exécution retourne à la fonction SaveRecords
, en écrivant Save Complete
dans la console. Une fois que tout dans SaveRecords
est terminé, l'exécution se poursuivra dans MyCallingFunction
à droite où c'était quand SaveChangesAsync()
a terminé. Confus? Voici un exemple de sortie:
Function Starting Save Starting Continuing to execute! Continuing to execute! Continuing to execute! Continuing to execute! Continuing to execute! .... Continuing to execute! Save Complete! Continuing to execute! Continuing to execute! Continuing to execute! .... Continuing to execute! Function Complete!
Ou peut-être:
Function Starting Save Starting Continuing to execute! Continuing to execute! Save Complete! Continuing to execute! Continuing to execute! Continuing to execute! .... Continuing to execute! Function Complete!
C'est la beauté de async await
, votre code peut continuer à fonctionner pendant que vous attendez que quelque chose se termine. En réalité, vous auriez une fonction plus comme celle-ci comme 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 d'enregistrement de sauvegarde différentes allant en même temps. {[23] } se terminera beaucoup plus rapidement en utilisant async await
alors si les fonctions SaveRecords
individuelles ont été appelées série.
La seule chose que je n'ai pas encore abordée est le mot-clé await
. Ce que cela fait, c'est arrêter l'exécution de la fonction actuelle jusqu'à ce que tout Task
que vous attendez se termine. Ainsi, dans le cas de l'original MyCallingFunction
, la ligne Function Complete
ne sera pas écrite dans la console tant que la fonction SaveRecords
ne sera pas terminée.
Bref, si vous avez une option à utiliser async await
, vous devriez car cela augmentera considérablement les performances de votre application.
Cette déclaration est incorrecte:
Côté serveur, lorsque nous utilisons des méthodes asynchrones, nous devons également ajouter await.
Vous n'avez pas besoin d'ajouter "en attente". "await" 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 souligné, vous pouvez accomplir cela simplement en appelant SaveChanges au lieu de SaveChangesAsync.
Mais fondamentalement, un appel asynchrone est beaucoup plus que cela. L'idée ici est que s'il y a d'autres travaux que vous pouvez faire (sur le serveur) pendant que L'opération de sauvegarde est en cours, vous devez utiliser SaveChangesAsync. N'utilisez pas "attendre". Appelez simplement SaveChangesAsync, puis continuez à faire d'autres choses en parallèle. Cela inclut potentiellement, dans une application web, le retour d'une réponse au client avant même la fin de la sauvegarde. Mais bien sûr, vous voudrez toujours vérifier le résultat final de la sauvegarde de sorte qu'en cas d'échec, vous pouvez le communiquer à votre utilisateur, ou journal en quelque sorte.