J'ai recherché les différences entre 2 paires ci-dessus mais je n'ai trouvé aucun article expliquant clairement à ce sujet ainsi que quand 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 des Async
méthodes, 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?
c#
entity-framework
async-await
Hien Tran
la source
la source
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()
etSaveChangesAsync()
comme exemple, mais il en va de même pourFind()
etFindAsync()
.Supposons que vous ayez 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(); }
Commencez par créer une instance de
MyEDM
, ajoutez la listemyList
à la tableMyTable
, puis appelezSaveChanges()
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 tant que la validation n'est pas terminée. Cela peut prendre du temps en fonction de ce que vous vous engagez. Si vous validez des modifications dans les enregistrements, l'entité doit les valider un par un (une fois, une sauvegarde a pris 2 minutes pour les mises à jour)!Pour résoudre ce problème, vous pouvez effectuer l'une des deux opérations suivantes. Le premier est que vous pouvez démarrer un nouveau fil pour gérer l'insert. Bien que cela libère le thread appelant pour continuer à s'exécuter, vous avez créé un nouveau thread qui va simplement rester là et attendre. Il n'y a pas besoin de cette surcharge, et c'est ce que le
async await
modèle résout.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 qu'elle soit:using(var context = new MyEDM()) { Console.WriteLine("Save Starting"); context.MyTable.AddRange(myList); await context.SaveChangesAsync(); Console.WriteLine("Save Complete"); }
C'est un tout petit changement, mais il y a des effets profonds sur l'efficacité et les performances de votre code. Alors que se passe-t-il? Le début du code est le même, vous créez une instance de
MyEDM
et ajoutez votremyList
àMyTable
. Mais lorsque vous appelezawait context.SaveChangesAsync()
, l'exécution du code revient à la fonction appelante! Ainsi, pendant que vous attendez que tous ces enregistrements soient validés, votre code peut continuer à s'exécuter. Dites que la fonction qui contenait le code ci-dessus avait la signature depublic 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. Voyons d'abord ce qui se passe.L'exécution entre
MyCallingFunction
,Function Starting
puisSave Starting
est écrite sur la console, puis la fonctionSaveChangesAsync()
est appelée. À ce stade, l'exécution retourneMyCallingFunction
et entre dans la boucle for en écrivant «Continuer à exécuter» jusqu'à 1000 fois. Une foisSaveChangesAsync()
terminé, l'exécution revient à laSaveRecords
fonction, en écrivantSave Complete
dans la console. Une fois que tout estSaveRecords
terminé, l'exécution se poursuivraMyCallingFunction
comme elle l'était à laSaveChangesAsync()
fin. Confus? Voici un exemple de sortie:Ou peut-être:
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 comme celle-ci que votre 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 disposez de quatre fonctions de sauvegarde d'enregistrement différentes en même temps .
MyCallingFunction
se terminera beaucoup plus rapidementasync await
que si lesSaveRecords
fonctions individuelles étaient appelées en série.La seule chose que je n'ai pas encore abordée est le
await
mot clé. Cela empêche la fonction en cours de s'exécuter jusqu'à ce que tout ce queTask
vous attendez soit terminé. Donc, dans le cas de l'originalMyCallingFunction
, la ligneFunction Complete
ne sera pas écrite sur la console tant que laSaveRecords
fonction ne sera pas terminée.Pour faire court, si vous avez une option à utiliser
async await
, vous devriez car cela augmentera considérablement les performances de votre application.la source
await
cependant, même si VOUS n'avez rien d'autre à faire après l'appel à SaveChanges, ASP dira "aha, ce thread est retourné en attente d'une opération asynchrone, cela signifie que je peux laisser ce thread gérer une autre requête en attendant ! " Cela améliore considérablement l'échelle horizontale de votre application.await
pourSaveChangesAsync
car EF ne prend pas en charge multiple enregistre en même temps. docs.microsoft.com/en-us/ef/core/saving/async En outre, il y a en fait un grand avantage à utiliser ces méthodes asynchrones. Par exemple, vous pouvez continuer à recevoir d'autres demandes dans votre webApi lorsque vous enregistrez des données ou faites beaucoup de sutuff, ou améliorez l'expérience utilisateur en ne gelant pas l'interface lorsque vous êtes dans une application de bureau.Ma dernière explication sera basée sur l'extrait de code suivant.
using System; using System.Threading; using System.Threading.Tasks; using static System.Console; public static class Program { const int N = 20; static readonly object obj = new object(); static int counter; public static void Job(ConsoleColor color, int multiplier = 1) { for (long i = 0; i < N * multiplier; i++) { lock (obj) { counter++; ForegroundColor = color; Write($"{Thread.CurrentThread.ManagedThreadId}"); if (counter % N == 0) WriteLine(); ResetColor(); } Thread.Sleep(N); } } static async Task JobAsync() { // intentionally removed } public static async Task Main() { // intentionally removed } }
Cas 1
static async Task JobAsync() { Task t = Task.Run(() => Job(ConsoleColor.Red, 1)); Job(ConsoleColor.Green, 2); await t; Job(ConsoleColor.Blue, 1); } public static async Task Main() { Task t = JobAsync(); Job(ConsoleColor.White, 1); await t; }
Remarques: Comme la partie synchrone (verte) de
JobAsync
tourne plus longtemps que la tâchet
(rouge), la tâchet
est déjà terminée au point deawait t
. En conséquence, la suite (bleue) fonctionne sur le même fil que le vert. La partie synchrone deMain
(blanche) tournera une fois la partie verte terminée. C'est pourquoi la partie synchrone en méthode asynchrone est problématique.Cas 2
static async Task JobAsync() { Task t = Task.Run(() => Job(ConsoleColor.Red, 2)); Job(ConsoleColor.Green, 1); await t; Job(ConsoleColor.Blue, 1); } public static async Task Main() { Task t = JobAsync(); Job(ConsoleColor.White, 1); await t; }
Remarques: Ce cas est opposé au premier cas. La partie synchrone (verte) des
JobAsync
spins plus courte que la tâchet
(rouge) alors la tâchet
n'est pas terminée au moment deawait t
. En conséquence, la suite (bleue) s'exécute sur le fil différent du fil vert. La partie synchrone deMain
(blanche) tourne toujours une fois que la partie verte a fini de tourner.Cas 3
static async Task JobAsync() { Task t = Task.Run(() => Job(ConsoleColor.Red, 1)); await t; Job(ConsoleColor.Green, 1); Job(ConsoleColor.Blue, 1); } public static async Task Main() { Task t = JobAsync(); Job(ConsoleColor.White, 1); await t; }
Remarques: Ce cas résoudra le problème dans les cas précédents concernant la partie synchrone en méthode asynchrone. La tâche
t
est immédiatement attendue. En conséquence, la suite (bleue) s'exécute sur le fil différent du fil vert. La partie synchrone deMain
(blanc) tournera immédiatement parallèlement àJobAsync
.Si vous souhaitez ajouter d'autres cas, n'hésitez pas à les modifier.
la source
Cette déclaration est incorrecte:
Vous n'avez pas besoin d'ajouter "await",
await
c'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 Save terminée. Mais comme vous l'avez souligné, vous pouvez accomplir cela simplement en appelantSaveChanges
au lieu deSaveChangesAsync
.Mais fondamentalement, un appel asynchrone est bien plus que cela. L'idée ici est que s'il y a un autre travail 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 simplementSaveChangesAsync
, puis continuez à faire d'autres choses en parallèle. Cela inclut potentiellement, dans une application Web, le renvoi d'une réponse au client avant même la fin de l'enregistrement. 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 consigner d'une manière ou d'une autre.la source