Requête asynchrone et enregistrement

Remarque

EF6 et versions ultérieures uniquement : Les fonctionnalités, les API, etc. décrites dans cette page ont été introduites dans Entity Framework 6. Si vous utilisez une version antérieure, certaines ou toutes les informations ne s’appliquent pas.

EF6 a introduit la prise en charge de la requête asynchrone et de l’enregistrement à l’aide des mots clés asynchrones et await qui ont été introduits dans .NET 4.5. Bien que toutes les applications ne bénéficient pas d’une synchronisation, elles peuvent être utilisées pour améliorer la réactivité du client et l’extensibilité du serveur lors de la gestion des tâches à long terme, réseau ou liées aux E/S.

Quand utiliser vraiment async

L’objectif de cette procédure pas à pas est d’introduire les concepts asynchrones d’une manière qui facilite l’observation de la différence entre l’exécution asynchrone et synchrone du programme. Cette procédure pas à pas n’est destinée à illustrer aucun des scénarios clés dans lesquels la programmation asynchrone offre des avantages.

La programmation asynchrone est principalement axée sur la libération du thread managé actuel (thread exécutant du code .NET) pour effectuer d’autres tâches pendant qu’elle attend une opération qui ne nécessite aucun temps de calcul à partir d’un thread managé. Par exemple, alors que le moteur de base de données traite une requête, il n’y a rien à faire par le code .NET.

Dans les applications clientes (WinForms, WPF, etc.), le thread actuel peut être utilisé pour maintenir la réactivité de l’interface utilisateur pendant que l’opération asynchrone est effectuée. Dans les applications serveur (ASP.NET etc.), le thread peut être utilisé pour traiter d’autres requêtes entrantes. Cela peut réduire l’utilisation de la mémoire et/ou augmenter le débit du serveur.

Dans la plupart des applications utilisant async, aucun avantage notable n’est visible et peut même être préjudiciable. Utilisez des tests, le profilage et le bon sens pour mesurer l’impact de l’async dans votre scénario particulier avant de vous y engager.

Voici quelques ressources supplémentaires pour en savoir plus sur async :

Créer le modèle

Nous allons utiliser le flux de travail Code First pour créer notre modèle et générer la base de données, mais les fonctionnalités asynchrones fonctionnent avec tous les modèles EF, y compris ceux créés avec EF Designer.

  • Créez une application console et appelez-la AsyncDemo
  • Ajouter le package NuGet EntityFramework
    • Dans l’Explorateur de solutions, cliquez avec le bouton droit sur le projet AsyncDemo
    • Sélectionnez Gérer les packages NuGet…
    • Dans la boîte de dialogue Gérer les packages NuGet, sélectionnez l’onglet Online, puis choisissez le package EntityFramework
    • Cliquez sur Install.
  • Ajouter une classe Model.cs avec l’implémentation suivante
    using System.Collections.Generic;
    using System.Data.Entity;

    namespace AsyncDemo
    {
        public class BloggingContext : DbContext
        {
            public DbSet<Blog> Blogs { get; set; }
            public DbSet<Post> Posts { get; set; }
        }

        public class Blog
        {
            public int BlogId { get; set; }
            public string Name { get; set; }

            public virtual List<Post> Posts { get; set; }
        }

        public class Post
        {
            public int PostId { get; set; }
            public string Title { get; set; }
            public string Content { get; set; }

            public int BlogId { get; set; }
            public virtual Blog Blog { get; set; }
        }
    }

 

Créer un programme synchrone

Maintenant que nous avons un modèle EF, nous allons écrire du code qui l’utilise pour effectuer un accès aux données.

  • Remplacez le contenu de Program.cs par le code suivant
    using System;
    using System.Linq;

    namespace AsyncDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                PerformDatabaseOperations();

                Console.WriteLine("Quote of the day");
                Console.WriteLine(" Don't worry about the world coming to an end today... ");
                Console.WriteLine(" It's already tomorrow in Australia.");

                Console.WriteLine();
                Console.WriteLine("Press any key to exit...");
                Console.ReadKey();
            }

            public static void PerformDatabaseOperations()
            {
                using (var db = new BloggingContext())
                {
                    // Create a new blog and save it
                    db.Blogs.Add(new Blog
                    {
                        Name = "Test Blog #" + (db.Blogs.Count() + 1)
                    });
                    Console.WriteLine("Calling SaveChanges.");
                    db.SaveChanges();
                    Console.WriteLine("SaveChanges completed.");

                    // Query for all blogs ordered by name
                    Console.WriteLine("Executing query.");
                    var blogs = (from b in db.Blogs
                                orderby b.Name
                                select b).ToList();

                    // Write all blogs out to Console
                    Console.WriteLine("Query completed with following results:");
                    foreach (var blog in blogs)
                    {
                        Console.WriteLine(" " + blog.Name);
                    }
                }
            }
        }
    }

Ce code appelle la méthode PerformDatabaseOperations qui enregistre une nouvelle Blog dans la base de données, puis récupère tous les Blogs à partir de la base de données et les imprime dans la console. Après cela, le programme écrit une citation du jour dans la console.

Étant donné que le code est synchrone, nous pouvons observer le flux d’exécution suivant lorsque nous exécutons le programme :

  1. SaveChanges commence à envoyer le nouveau Blog à la base de données
  2. SaveChanges se termine
  3. La requête pour tous les blogs est envoyée à la base de données
  4. Les retours de requête et les résultats sont écrits dans console
  5. La citation du jour est écrite dans console

Sync Output 

 

En le rendant asynchrone

Maintenant que nous avons notre programme opérationnel, nous pouvons commencer à utiliser les nouveaux mots clés asynchrones et await. Nous avons apporté les modifications suivantes à Program.cs

  1. Ligne 2 : L’instruction using pour l’espace de noms System.Data.Entity nous donne accès aux méthodes d’extension asynchrone EF.
  2. Ligne 4 : L’instruction using pour l’espace de noms System.Threading.Tasks nous permet d’utiliser le type de Task.
  3. Ligne 12 et 18 : Nous capturons en tant que tâche qui surveille la progression de PerformSomeDatabaseOperations (ligne 12), puis nous bloquons l’exécution du programme pour que cette tâche s’achève une fois que tous les travaux du programme sont terminés (ligne 18).
  4. Ligne 25 : Nous avons mis à jour PerformSomeDatabaseOperations être marqués comme async et renvoyer un Task.
  5. Ligne 35 : Nous appelons maintenant la version asynchrone de SaveChanges et attendons qu’elle soit terminée.
  6. Ligne 42 : Nous appelons maintenant la version asynchrone de ToList et attendons le résultat.

Pour obtenir la liste complète des méthodes d’extension disponibles dans l’espace de noms System.Data.Entity, reportez-vous à la classe QueryableExtensions . Vous devez également ajouter using System.Data.Entity à vos instructions using.

    using System;
    using System.Data.Entity;
    using System.Linq;
    using System.Threading.Tasks;

    namespace AsyncDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                var task = PerformDatabaseOperations();

                Console.WriteLine("Quote of the day");
                Console.WriteLine(" Don't worry about the world coming to an end today... ");
                Console.WriteLine(" It's already tomorrow in Australia.");

                task.Wait();

                Console.WriteLine();
                Console.WriteLine("Press any key to exit...");
                Console.ReadKey();
            }

            public static async Task PerformDatabaseOperations()
            {
                using (var db = new BloggingContext())
                {
                    // Create a new blog and save it
                    db.Blogs.Add(new Blog
                    {
                        Name = "Test Blog #" + (db.Blogs.Count() + 1)
                    });
                    Console.WriteLine("Calling SaveChanges.");
                    await db.SaveChangesAsync();
                    Console.WriteLine("SaveChanges completed.");

                    // Query for all blogs ordered by name
                    Console.WriteLine("Executing query.");
                    var blogs = await (from b in db.Blogs
                                orderby b.Name
                                select b).ToListAsync();

                    // Write all blogs out to Console
                    Console.WriteLine("Query completed with following results:");
                    foreach (var blog in blogs)
                    {
                        Console.WriteLine(" - " + blog.Name);
                    }
                }
            }
        }
    }

Maintenant que le code est asynchrone, nous pouvons observer un flux d’exécution différent lorsque nous exécutons le programme :

  1. SaveChanges commence à envoyer le nouveau Blog à la base de données
    Une fois la commande envoyée à la base de données, il n’est plus nécessaire de temps de calcul sur le thread managé actuel. La méthode PerformDatabaseOperations retourne (même si elle n’a pas terminé l’exécution) et le flux de programme dans la méthode Principal continue.
  2. Citation du jour est écrite dans la console
    Étant donné qu’il n’y a plus de travail à effectuer dans la méthode Main, le thread managé est bloqué sur l’appel Wait jusqu’à ce que l’opération de base de données se termine. Une fois l’opération terminée, le reste de notre PerformDatabaseOperations sera exécuté.
  3. SaveChanges se termine
  4. La requête pour tous les blogs est envoyée à la base de données
    Là encore, le thread managé est libre d’effectuer d’autres tâches pendant que la requête est traitée dans la base de données. Étant donné que toutes les autres exécutions sont terminées, le thread s’arrête simplement sur l’appel d’attente.
  5. Les retours de requête et les résultats sont écrits dans console

Async Output 

 

Le takeaway

Nous avons vu maintenant comment il est facile d’utiliser des méthodes asynchrones EF. Bien que les avantages d’async ne soient peut-être pas très apparents avec une application console simple, ces mêmes stratégies peuvent être appliquées dans des situations où des activités de longue durée ou liées au réseau peuvent autrement bloquer l’application, ou entraîner un grand nombre de threads pour augmenter l’empreinte mémoire.