Consulta asincrónica y guardado

Nota

Solo EF6 y versiones posteriores: las características, las API, etc. que se tratan en esta página se han incluido a partir de Entity Framework 6. Si usa una versión anterior, no se aplica parte o la totalidad de la información.

EF6 introdujo compatibilidad con consultas asincrónicas y guardar con las palabras clave async y await que se introdujeron en .NET 4.5. Aunque no todas las aplicaciones pueden beneficiarse de la asincronía, se puede usar para mejorar la capacidad de respuesta del cliente y la escalabilidad del servidor al controlar tareas enlazadas a E/S o de larga duración.

Cuándo usar realmente async

El propósito de este tutorial es introducir los conceptos asincrónicos de una manera que facilita la observación de la diferencia entre la ejecución de programas asincrónica y sincrónica. Este tutorial no está pensado para ilustrar ninguno de los escenarios clave en los que la programación asincrónica proporciona ventajas.

La programación asincrónica se centra principalmente en liberar el subproceso administrado actual (subproceso que ejecuta código de .NET) para realizar otro trabajo mientras espera una operación que no requiere ningún tiempo de proceso desde un subproceso administrado. Por ejemplo, mientras que el motor de base de datos está procesando una consulta, el código de .NET no hace nada.

En las aplicaciones cliente (WinForms, WPF, etc.), el subproceso actual se puede usar para mantener la interfaz de usuario con capacidad de respuesta mientras se realiza la operación asincrónica. En las aplicaciones de servidor (ASP.NET etc.), el subproceso se puede usar para procesar otras solicitudes entrantes, lo que puede reducir el uso de memoria o aumentar el rendimiento del servidor.

En la mayoría de las aplicaciones que usan async no tendrán ventajas notables e incluso podrían ser perjudiciales. Use pruebas, generación de perfiles y sentido común para medir el impacto de async en su escenario concreto antes de confirmarlo.

Estos son algunos recursos más para obtener información sobre async:

Creación del modelo

Usaremos el flujo de trabajo code first para crear nuestro modelo y generar la base de datos, pero la funcionalidad asincrónica funcionará con todos los modelos de EF, incluidos los creados con ef Designer.

  • Crear una aplicación de consola y llamarla AsyncDemo
  • Adición del paquete NuGet EntityFramework
    • En Explorador de soluciones, haga clic con el botón derecho en el proyecto AsyncDemo.
    • Seleccione Administrar paquetes NuGet...
    • En el cuadro de diálogo Administrar paquetes NuGet, seleccione la pestaña En línea y elija el paquete EntityFramework .
    • Haz clic en Instalar
  • Adición de una clase Model.cs con la siguiente implementación
    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; }
        }
    }

 

Crear un programa sincrónico

Ahora que tenemos un modelo de EF, vamos a escribir código que lo usa para realizar algún acceso a datos.

  • Reemplace el contenido de Program.cs por el código siguiente.
    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);
                    }
                }
            }
        }
    }

Este código llama al PerformDatabaseOperations método que guarda un nuevo blog en la base de datos y, a continuación, recupera todos los blogs de la base de datos e los imprime en la consola. Después de esto, el programa escribe una cita del día en la Consola.

Dado que el código es sincrónico, podemos observar el siguiente flujo de ejecución al ejecutar el programa:

  1. SaveChanges comienza a insertar el nuevo blog en la base de datos
  2. SaveChanges Completa
  3. La consulta de todos los blogs se envía a la base de datos.
  4. La consulta devuelve y los resultados se escriben en la consola
  5. La cita del día se escribe en la consola

Salida de sincronización 

 

Hacer que sea asincrónico

Ahora que tenemos nuestro programa en funcionamiento, podemos empezar a usar las nuevas palabras clave asincrónicas y await. Hemos realizado los siguientes cambios en Program.cs

  1. Línea 2: La instrucción using del System.Data.Entity espacio de nombres nos proporciona acceso a los métodos de extensión asincrónicos de EF.
  2. Línea 4: la instrucción using para el System.Threading.Tasks espacio de nombres nos permite usar el Task tipo .
  3. Línea 12 & 18: Estamos capturando como tarea que supervisa el progreso de PerformSomeDatabaseOperations (línea 12) y, a continuación, bloqueando la ejecución del programa para que esta tarea se complete una vez que se complete todo el trabajo del programa (línea 18).
  4. Línea 25: Hemos actualizado PerformSomeDatabaseOperations para que se marque como async y devuelva un Task.
  5. Línea 35: Ahora estamos llamando a la versión asincrónica de SaveChanges y esperando su finalización.
  6. Línea 42: Ahora estamos llamando a la versión asincrónica de ToList y a la espera del resultado.

Para obtener una lista completa de los métodos de extensión disponibles en el System.Data.Entity espacio de nombres, consulte la QueryableExtensions clase . También deberá agregar using System.Data.Entity a las instrucciones 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);
                    }
                }
            }
        }
    }

Ahora que el código es asincrónico, podemos observar un flujo de ejecución diferente al ejecutar el programa:

  1. SaveChanges comienza a insertar el nuevo blog en la base de datos
    Una vez que el comando se envía a la base de datos, no se necesita más tiempo de proceso en el subproceso administrado actual. El PerformDatabaseOperations método devuelve (aunque no haya terminado de ejecutarse) y el flujo de programa en el método Main continúa.
  2. La cita del día se escribe en la consola
    Puesto que no hay más trabajo que hacer en el método Main, el subproceso administrado se bloquea en la Wait llamada hasta que se completa la operación de base de datos. Una vez completado, se ejecutará el resto de nuestra PerformDatabaseOperations .
  3. SaveChanges Completa
  4. La consulta de todos los blogs se envía a la base de datos.
    De nuevo, el subproceso administrado es libre de realizar otro trabajo mientras la consulta se procesa en la base de datos. Dado que la otra ejecución se ha completado, el subproceso se detendrá simplemente en la llamada Wait.
  5. La consulta devuelve y los resultados se escriben en la consola

Salida asincrónica 

 

La conclusión

Ahora vimos lo fácil que es usar los métodos asincrónicos de EF. Aunque es posible que las ventajas de async no sean muy evidentes con una aplicación de consola simple, estas mismas estrategias se pueden aplicar en situaciones en las que las actividades enlazadas a la red o de larga duración podrían bloquear la aplicación o provocar un gran número de subprocesos para aumentar la superficie de memoria.