Freigeben über


Asynchrone Abfrage und Speichern

Hinweis

Nur EF6 und höher: Die Features, APIs usw., die auf dieser Seite erläutert werden, wurden in Entity Framework 6 eingeführt. Wenn Sie eine frühere Version verwenden, gelten einige oder alle Informationen nicht.

EF6 hat Unterstützung für asynchrone Abfragen und Speichern eingeführt, mithilfe der Schlüsselwörter async und await, die in .NET 4.5 eingeführt wurden. Nicht alle Anwendungen profitieren von Asynchronität, aber sie kann verwendet werden, um die Reaktionsfähigkeit des Clients und die Serverskalierbarkeit bei der Behandlung von langen Ausführungs-, Netzwerk- oder E/A-gebundenen Aufgaben zu verbessern.

Wann soll asynchron verwendet werden?

In dieser exemplarischen Vorgehensweise werden die asynchronen Konzepte auf eine Weise eingeführt, mit der der Unterschied zwischen asynchroner und synchroner Programmausführung leicht zu beobachten ist. Diese exemplarische Vorgehensweise soll keines der wichtigsten Szenarien veranschaulichen, in denen die asynchrone Programmierung Vorteile bietet.

Die asynchrone Programmierung konzentriert sich in erster Linie darauf, den aktuellen verwalteten Thread (Thread mit .NET-Code) freizugeben, um andere Aufgaben auszuführen, während er auf einen Vorgang wartet, der keine Berechnungszeit von einem verwalteten Thread erfordert. Während das Datenbankmodul beispielsweise eine Abfrage verarbeitet, gibt es nichts, das von .NET-Code ausgeführt werden muss.

In Clientanwendungen (WinForms, WPF usw.) kann der aktuelle Thread verwendet werden, um die Benutzeroberfläche reaktionsfähig zu halten, während der asynchrone Vorgang ausgeführt wird. In Serveranwendungen (ASP.NET usw.) kann der Thread zum Verarbeiten anderer eingehender Anforderungen verwendet werden. Dies kann die Speicherauslastung reduzieren und/oder den Durchsatz des Servers erhöhen.

In den meisten Anwendungen, bei der Verwendung von async, gibt es keine spürbaren Vorteile und es kann sogar nachteilig sein. Verwenden Sie Tests, Profilerstellung und gesunden Menschenverstand, um die Auswirkungen von Asynchronität in Ihrem jeweiligen Szenario zu messen, bevor Sie sich darauf festlegen.

Hier finden Sie einige weitere Ressourcen, um mehr über asynchrone Programmierung zu erfahren:

Erstellen des Modells

Wir verwenden den Code First-Workflow , um unser Modell zu erstellen und die Datenbank zu generieren. Die asynchrone Funktionalität funktioniert jedoch mit allen EF-Modellen, einschließlich derjenigen, die mit dem EF Designer erstellt wurden.

  • Erstellen einer Konsolenanwendung und Aufrufen von "AsyncDemo"
  • Hinzufügen des EntityFramework NuGet-Pakets
    • Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das AsyncDemo-Projekt .
    • Wählen Sie „NuGet-Pakete verwalten…“
    • Wählen Sie im Dialogfeld "NuGet-Pakete verwalten" die Registerkarte "Online " und dann das EntityFramework-Paket aus.
    • Klicken Sie auf Install (Installieren).
  • Hinzufügen einer Model.cs Klasse mit der folgenden Implementierung
    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; }
        }
    }

 

Erstellen eines synchronen Programms

Nachdem wir nun über ein EF-Modell verfügen, schreiben wir code, der ihn zum Ausführen eines Datenzugriffs verwendet.

  • Ersetzen Sie den Inhalt von Program.cs durch den folgenden Code.
    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);
                    }
                }
            }
        }
    }

Dieser Code ruft die Methode auf, mit der PerformDatabaseOperations ein neuer Blog in der Datenbank gespeichert wird, und ruft dann alle Blogs aus der Datenbank ab und druckt sie in der Konsole. Danach schreibt das Programm ein Zitat des Tages in die Konsole.

Da der Code synchron ist, können wir den folgenden Ausführungsfluss beobachten, wenn wir das Programm ausführen:

  1. SaveChanges beginnt mit dem Pushen des neuen Blogs an die Datenbank.
  2. SaveChanges abschließen
  3. Abfrage für alle Blogs wird an die Datenbank gesendet
  4. Abfrageergebnisse und Resultate werden in die Konsole geschrieben.
  5. Das Zitat des Tages wird auf die Konsole geschrieben

Synchronisierungsausgabe  

 

Asynchrone Erstellung

Nachdem unser Programm nun läuft, können wir mit der Nutzung der neuen async und await Schlüsselwörter beginnen. Wir haben die folgenden Änderungen an der Datei Program.cs vorgenommen.

  1. Zeile 2: Die using-Anweisung für den System.Data.Entity Namespace gibt uns Zugriff auf die EF asynchronen Erweiterungsmethoden.
  2. Zeile 4: Die using-Anweisung für den System.Threading.Tasks Namespace ermöglicht es uns, den Task Typ zu verwenden.
  3. Zeile 12 & 18: Wir erfassen eine Aufgabe, die den Fortschritt von PerformSomeDatabaseOperations (Zeile 12) überwacht und blockieren dann die Programmausführung, bis diese Aufgabe abgeschlossen ist, nachdem alle Arbeiten für das Programm erledigt sind (Zeile 18).
  4. Zeile 25: Wir haben PerformSomeDatabaseOperations aktualisiert, damit es als async gekennzeichnet wird und Task zurückgibt.
  5. Zeile 35: Wir rufen jetzt die asynchrone Version von SaveChanges auf und warten auf den Abschluss.
  6. Zeile 42: Wir rufen jetzt die Asynchrone Version von ToList und warten auf das Ergebnis.

Eine umfassende Liste der verfügbaren Erweiterungsmethoden im System.Data.Entity Namespace finden Sie in der QueryableExtensions Klasse. Außerdem müssen Sie using System.Data.Entity zu Ihren using-Anweisungen hinzufügen.

    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);
                    }
                }
            }
        }
    }

Da der Code asynchron ist, können wir beim Ausführen des Programms einen anderen Ausführungsfluss beobachten:

  1. SaveChanges beginnt mit dem Pushen des neuen Blogs an die Datenbank.
    Sobald der Befehl an die Datenbank gesendet wird, ist keine Berechnungszeit für den aktuellen verwalteten Thread erforderlich. Die PerformDatabaseOperations Methode gibt zurück (auch wenn sie nicht abgeschlossen wurde) und der Programmfluss in der Main-Methode fortgesetzt wird.
  2. Das Zitat des Tages wird in Konsole geschrieben
    Da in der Main-Methode keine weiteren Aufgaben ausgeführt werden müssen, wird der verwaltete Thread für den Wait Aufruf blockiert, bis der Datenbankvorgang abgeschlossen ist. Nach Abschluss des Vorgangs wird der Rest unseres PerformDatabaseOperations Vorgangs ausgeführt.
  3. SaveChanges abschließen
  4. Abfrage für alle Blogs wird an die Datenbank gesendet
    Auch hier kann der verwaltete Thread andere Aufgaben ausführen, während die Abfrage in der Datenbank verarbeitet wird. Da alle anderen Ausführungen abgeschlossen sind, wird der Thread beim Wait-Aufruf einfach anhalten.
  5. Abfrageergebnisse und Resultate werden in die Konsole geschrieben.

Asynchrone Ausgabe  

 

Die Erkenntnis

Wir haben nun gesehen, wie einfach es ist, die asynchronen Methoden von EF zu nutzen. Obwohl die Vorteile von async bei einer einfachen Konsolen-App möglicherweise nicht sehr offensichtlich sind, können dieselben Strategien in Situationen angewendet werden, in denen lange ausgeführte oder netzwerkgebundene Aktivitäten die Anwendung sonst blockieren oder eine große Anzahl von Threads dazu führen, den Speicherbedarf zu erhöhen.