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?

59
demandé sur JRLambert 2015-05-05 04:26:07

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.

112
répondu JRLambert 2015-06-26 04:52:13

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.

3
répondu Rajeev Goel 2015-05-05 02:06:36