Asynkronisera fråga och spara

Anmärkning

ENDAST EF6 – De funktioner, API:er osv. som beskrivs på den här sidan introducerades i Entity Framework 6. Om du använder en tidigare version gäller inte en del av eller all information.

EF6 introducerade stöd för asynkrona frågor och sparar med hjälp av asynkrona och invänta nyckelord som introducerades i .NET 4.5. Även om inte alla program kan dra nytta av asynkron användning kan det användas för att förbättra klientens svarstider och serverns skalbarhet vid hantering av långvariga uppgifter, nätverk eller I/O-bundna uppgifter.

När du verkligen ska använda asynkronisering

Syftet med den här genomgången är att introducera asynkrona begrepp på ett sätt som gör det enkelt att observera skillnaden mellan asynkron och synkron programkörning. Den här genomgången är inte avsedd att illustrera något av de viktigaste scenarierna där asynkron programmering ger fördelar.

Async-programmering fokuserar främst på att frigöra den aktuella hanterade tråden (tråd som kör .NET-kod) för att utföra annat arbete medan den väntar på en åtgärd som inte kräver någon beräkningstid från en hanterad tråd. När databasmotorn till exempel bearbetar en fråga finns det inget att göra med .NET-kod.

I klientprogram (WinForms, WPF osv.) kan den aktuella tråden användas för att hålla användargränssnittet dynamiskt medan asynkroniseringsåtgärden utförs. I serverprogram (ASP.NET osv.) kan tråden användas för att bearbeta andra inkommande begäranden – detta kan minska minnesanvändningen och/eller öka dataflödet för servern.

I de flesta program som använder asynkronisering har inga märkbara fördelar och kan till och med vara skadliga. Använd tester, profilering och sunt förnuft för att mäta effekten av asynkronisering i ditt specifika scenario innan du förbinder dig till det.

Här är några fler resurser att lära dig om asynkronisering:

Skapa modellen

Vi kommer att använda code first-arbetsflödet för att skapa vår modell och generera databasen, men de asynkrona funktionerna fungerar med alla EF-modeller, inklusive de som skapats med EF Designer.

  • Skapa ett konsolprogram och kalla det AsyncDemo
  • Lägg till EntityFramework NuGet-paketet
    • Högerklicka på AsyncDemo-projektet i Solution Explorer
    • Välj Hantera NuGet-paket...
    • I dialogrutan Hantera NuGet-paket väljer du fliken Online och väljer EntityFramework-paketet
    • Klicka på Installera
  • Lägg till en Model.cs-klass med följande implementering
    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; }
        }
    }

 

Skapa ett synkront program

Nu när vi har en EF-modell ska vi skriva kod som använder den för att utföra viss dataåtkomst.

  • Ersätt innehållet i Program.cs med följande kod
    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);
                    }
                }
            }
        }
    }

Den här koden anropar metoden PerformDatabaseOperations som sparar en ny blogg i databasen och sedan hämtar alla bloggar från databasen och skriver ut dem till konsolen. Därefter skriver programmet dagens offert till konsolen.

Eftersom koden är synkron kan vi observera följande körningsflöde när vi kör programmet:

  1. SaveChanges börjar skicka den nya bloggen till databasen
  2. SaveChanges Avslutar
  3. Frågan för alla bloggar skickas till databasen
  4. Frågereturer och resultat skrivs till konsolen
  5. Dagens citat visas på Konsolen

Synkronisera utdata  

 

Att göra det asynkront

Nu när programmet är igång kan vi börja använda den nya asynkronisering och vänta på nyckelord. Vi har gjort följande ändringar i Program.cs

  1. Rad 2: Använder-satsen System.Data.Entity för namnområdet ger oss åtkomst till EF asynkrona tilläggsmetoder.
  2. Rad 4: Using-satsen för namnområdet System.Threading.Tasks tillåter oss att använda typen Task.
  3. Rad 12 & 18: Vi fångar en uppgift som övervakar förloppet av PerformSomeDatabaseOperations (rad 12) och sedan blockerar vi programkörningen tills denna uppgift har slutförts när allt arbete för programmet är klart (rad 18).
  4. Rad 25: Vi har uppdaterat PerformSomeDatabaseOperations för att markeras som async och returnera en Task.
  5. Rad 35: Vi anropar nu Async-versionen av SaveChanges och väntar på att den ska slutföras.
  6. Rad 42: Vi anropar nu Async-versionen av ToList och väntar på resultatet.

En omfattande lista över tillgängliga tilläggsmetoder i namnområdet finns i System.Data.EntityQueryableExtensions klassen . Du måste också lägga using System.Data.Entity till i dina användningsuttryck.

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

Nu när koden är asynkron kan vi observera ett annat körningsflöde när vi kör programmet:

  1. SaveChanges börjar skicka den nya bloggen till databasen
    När kommandot har skickats till databasen behövs ingen mer beräkningstid i den aktuella hanterade tråden. Metoden PerformDatabaseOperations returnerar (även om den inte har körts klart) och programflödet i Main-metoden fortsätter.
  2. Dagens citat skrivs ut till konsolen
    Eftersom det inte finns mer arbete att göra i main-metoden blockeras den hanterade tråden i anropet Wait tills databasåtgärden har slutförts. När processen är klar kommer resten av vårt PerformDatabaseOperations att köras.
  3. SaveChanges Avslutar
  4. Frågan för alla bloggar skickas till databasen
    Återigen är den hanterade tråden fri att utföra annat arbete medan frågan bearbetas i databasen. Eftersom all annan körning har slutförts, kommer tråden helt enkelt att stanna vid vänteanropet.
  5. Frågereturer och resultat skrivs till konsolen

Async Output  

 

Slutsats

Nu har vi sett hur enkelt det är att använda EF:s asynkrona metoder. Även om fördelarna med asynkronisering kanske inte är särskilt uppenbara med en enkel konsolapp, kan samma strategier tillämpas i situationer där långvariga eller nätverksbundna aktiviteter annars kan blockera programmet, eller orsaka ett stort antal trådar för att öka minnesfotavtrycket.