Query asincrona e salvataggio

Nota

Solo EF6 e versioni successive: funzionalità, API e altri argomenti discussi in questa pagina sono stati introdotti in Entity Framework 6. Se si usa una versione precedente, le informazioni qui riportate, o parte di esse, non sono applicabili.

EF6 ha introdotto il supporto per la query asincrona e il salvataggio usando le parole chiave async e await introdotte in .NET 4.5. Anche se non tutte le applicazioni possono trarre vantaggio dall'asincronia, può essere usata per migliorare la velocità di risposta dei client e la scalabilità del server quando si gestiscono attività associate a esecuzione prolungata, rete o I/O.

Quando usare realmente asincrona

Lo scopo di questa procedura dettagliata è introdurre i concetti asincroni in modo da semplificare l'osservazione della differenza tra l'esecuzione asincrona e sincrona del programma. Questa procedura dettagliata non è progettata per illustrare gli scenari chiave in cui la programmazione asincrona offre vantaggi.

La programmazione asincrona si concentra principalmente sul liberare il thread gestito corrente (thread che esegue codice .NET) per eseguire altre operazioni mentre attende un'operazione che non richiede alcun tempo di calcolo da un thread gestito. Ad esempio, mentre il motore di database sta elaborando una query, non è necessario eseguire alcuna operazione dal codice .NET.

Nelle applicazioni client (WinForms, WPF e così via) il thread corrente può essere usato per mantenere reattiva l'interfaccia utente mentre viene eseguita l'operazione asincrona. Nelle applicazioni server (ASP.NET e così via) il thread può essere usato per elaborare altre richieste in ingresso. Ciò può ridurre l'utilizzo della memoria e/o aumentare la velocità effettiva del server.

Nella maggior parte delle applicazioni che usano asincrona non avranno vantaggi evidenti e potrebbero anche essere dannosi. Usare test, profilatura e buon senso per misurare l'impatto dell'asincrona nello scenario specifico prima di eseguirne il commit.

Ecco alcune altre risorse per informazioni su async:

Creare il modello

Verrà usato il flusso di lavoro Code First per creare il modello e generare il database, ma la funzionalità asincrona funzionerà con tutti i modelli di Entity Framework, inclusi quelli creati con Entity Framework Designer.

  • Creare un'applicazione console e chiamarla AsyncDemo
  • Aggiungere il pacchetto NuGet EntityFramework
    • In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto AsyncDemo
    • Selezionare Gestisci pacchetti NuGet...
    • Nella finestra di dialogo Gestisci pacchetti NuGet selezionare la scheda Online e scegliere il pacchetto EntityFramework
    • Fai clic su Install (Installa).
  • Aggiungere una classe Model.cs con l'implementazione seguente
    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; }
        }
    }

 

Creare un programma sincrono

Ora che è disponibile un modello di Entity Framework, è possibile scrivere codice che lo usa per eseguire l'accesso ai dati.

  • Sostituire il contenuto di Program.cs con il codice seguente
    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);
                    }
                }
            }
        }
    }

Questo codice chiama il PerformDatabaseOperations metodo che salva un nuovo blog nel database e quindi recupera tutti i blog dal database e li stampa nella console. Successivamente, il programma scrive una citazione del giorno nella console.

Poiché il codice è sincrono, è possibile osservare il flusso di esecuzione seguente quando si esegue il programma:

  1. SaveChanges inizia a eseguire il push del nuovo blog nel database
  2. SaveChanges Completa
  3. La query per tutti i blog viene inviata al database
  4. La query restituisce e i risultati vengono scritti nella console
  5. La citazione del giorno viene scritta nella console

Sync Output 

 

Renderlo asincrono

Ora che il programma è operativo, è possibile iniziare a usare le nuove parole chiave asincrone e await. Sono state apportate le modifiche seguenti a Program.cs

  1. Riga 2: l'istruzione using per lo System.Data.Entity spazio dei nomi consente di accedere ai metodi di estensione asincrona di ENTITY.
  2. Riga 4: l'istruzione using per lo System.Threading.Tasks spazio dei nomi consente di usare il Task tipo .
  3. Riga 12 & 18: Stiamo acquisendo come attività che monitora lo stato di PerformSomeDatabaseOperations avanzamento (riga 12) e quindi bloccando l'esecuzione del programma per completare l'attività una volta completato tutto il lavoro per il programma (riga 18).
  4. Riga 25: è stato eseguito l'aggiornamento PerformSomeDatabaseOperations per essere contrassegnato come async e viene restituito un oggetto Task.
  5. Riga 35: Ora stiamo chiamando la versione asincrona di SaveChanges e in attesa del completamento.
  6. Riga 42: Viene ora chiamata la versione asincrona di ToList e in attesa del risultato.

Per un elenco completo dei metodi di estensione disponibili nello spazio dei System.Data.Entity nomi, vedere la QueryableExtensions classe . Sarà anche necessario aggiungere using System.Data.Entity alle istruzioni 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);
                    }
                }
            }
        }
    }

Ora che il codice è asincrono, è possibile osservare un flusso di esecuzione diverso quando si esegue il programma:

  1. SaveChanges inizia a eseguire il push del nuovo blog nel database
    Dopo che il comando viene inviato al database non è necessario più tempo di calcolo nel thread gestito corrente. Il PerformDatabaseOperations metodo restituisce (anche se non è stata completata l'esecuzione) e il flusso del programma nel metodo Main continua.
  2. La citazione del giorno viene scritta nella console
    Poiché nel metodo Main non è più necessario eseguire altre operazioni, il thread gestito viene bloccato nella Wait chiamata fino al completamento dell'operazione di database. Al termine, il resto dell'oggetto PerformDatabaseOperations verrà eseguito.
  3. SaveChanges Completa
  4. La query per tutti i blog viene inviata al database
    Anche in questo caso, il thread gestito è libero di eseguire altre operazioni mentre la query viene elaborata nel database. Poiché tutte le altre esecuzioni sono state completate, il thread si interromperà solo sulla chiamata Wait.
  5. La query restituisce e i risultati vengono scritti nella console

Async Output 

 

Il takeaway

Ora è stato illustrato quanto sia facile usare i metodi asincroni di Entity Framework. Anche se i vantaggi di async potrebbero non essere molto evidenti con una semplice app console, queste stesse strategie possono essere applicate in situazioni in cui le attività a esecuzione prolungata o associato alla rete potrebbero altrimenti bloccare l'applicazione o causare un numero elevato di thread per aumentare il footprint di memoria.