tutorial transaction framework first code async c# entity-framework async-await

c# - transaction - Entity Framework Queryable async



iqueryable async (2)

El problema parece ser que ha entendido mal cómo funciona async / wait con Entity Framework.

Sobre Entity Framework

Entonces, veamos este código:

public IQueryable<URL> GetAllUrls() { return context.Urls.AsQueryable(); }

y ejemplo de uso:

repo.GetAllUrls().Where(u => <condition>).Take(10).ToList()

Que pasa alli

  1. Estamos obteniendo el objeto repo.GetAllUrls() todavía no repo.GetAllUrls() a la base de datos) usando repo.GetAllUrls()
  2. Creamos un nuevo objeto .Where(u => <condition> con condición especificada usando .Where(u => <condition>
  3. Creamos un nuevo objeto IQueryable con límite de paginación especificado usando .Take(10)
  4. Recuperamos resultados de la base de datos usando .ToList() . Nuestro objeto IQueryable se compila en sql (como select top 10 * from Urls where <condition> ). Y la base de datos puede usar índices, el servidor SQL solo le envía 10 objetos de su base de datos (no todos los mil millones de URL almacenados en la base de datos)

Bien, veamos el primer código:

public async Task<IQueryable<URL>> GetAllUrlsAsync() { var urls = await context.Urls.ToListAsync(); return urls.AsQueryable(); }

Con el mismo ejemplo de uso obtuvimos:

  1. Estamos cargando en memoria todos los miles de millones de URL almacenadas en su base de datos usando el await context.Urls.ToListAsync(); .
  2. Tenemos desbordamiento de memoria. La forma correcta de matar a tu servidor

Sobre async / await

¿Por qué se prefiere usar async / await? Veamos este código:

var stuff1 = repo.GetStuff1ForUser(userId); var stuff2 = repo.GetStuff2ForUser(userId); return View(new Model(stuff1, stuff2));

¿Qué pasa aquí?

  1. Comenzando en la línea 1 var stuff1 = ...
  2. Enviamos una solicitud al servidor sql de que queremos obtener algunas cosas1 para userId
  3. Esperamos (el hilo actual está bloqueado)
  4. Esperamos (el hilo actual está bloqueado)
  5. .....
  6. Servidor SQL envíenos una respuesta
  7. Pasamos a la línea 2 var stuff2 = ...
  8. Enviamos una solicitud al servidor sql de que queremos obtener algunas userId para userId
  9. Esperamos (el hilo actual está bloqueado)
  10. Y otra vez
  11. .....
  12. Servidor SQL envíenos una respuesta
  13. Representamos la vista

Así que veamos una versión asíncrona:

var stuff1Task = repo.GetStuff1ForUserAsync(userId); var stuff2Task = repo.GetStuff2ForUserAsync(userId); await Task.WhenAll(stuff1Task, stuff2Task); return View(new Model(stuff1Task.Result, stuff2Task.Result));

¿Qué pasa aquí?

  1. Enviamos una solicitud al servidor SQL para obtener cosas1 (línea 1)
  2. Enviamos solicitud al servidor SQL para obtener cosas2 (línea 2)
  3. Esperamos las respuestas del servidor SQL, pero el hilo actual no está bloqueado, puede manejar consultas de otros usuarios
  4. Representamos la vista

Forma correcta de hacerlo

Tan buen código aquí:

using System.Data.Entity; public IQueryable<URL> GetAllUrls() { return context.Urls.AsQueryable(); } public async Task<List<URL>> GetAllUrlsByUser(int userId) { return await GetAllUrls().Where(u => u.User.Id == userId).ToListAsync(); }

Tenga en cuenta que debe agregar using System.Data.Entity para usar el método ToListAsync() para IQueryable.

Tenga en cuenta que si no necesita filtrado, paginación y demás, no necesita trabajar con IQueryable . Puede usar await context.Urls.ToListAsync() y trabajar con la List<Url> materializada List<Url> .

Estoy trabajando en algunas cosas de API web usando Entity Framework 6 y uno de mis métodos de controlador es un "Obtener todo" que espera recibir el contenido de una tabla de mi base de datos como IQueryable<Entity> . En mi repositorio, me pregunto si hay alguna razón ventajosa para hacer esto de forma asincrónica, ya que soy nuevo en el uso de EF con async.

Básicamente se reduce a

public async Task<IQueryable<URL>> GetAllUrlsAsync() { var urls = await context.Urls.ToListAsync(); return urls.AsQueryable(); }

vs

public IQueryable<URL> GetAllUrls() { return context.Urls.AsQueryable(); }

¿La versión asíncrona realmente generará beneficios de rendimiento aquí o incurriré en gastos indirectos innecesarios al proyectar primero en una Lista (usando asíncrono) y luego ir a IQueryable?


Hay una gran diferencia en el ejemplo que ha publicado, la primera versión:

var urls = await context.Urls.ToListAsync();

Esto es malo , básicamente select * from table , devuelve todos los resultados a la memoria y luego aplica el where contra eso en la colección de memoria en lugar de select * from table where... contra la base de datos.

El segundo método no llegará a la base de datos hasta que se aplique una consulta a IQueryable (probablemente a través de una operación de .Where().Select() linq .Where().Select() que solo devolverá los valores de db que coinciden con la consulta.

Si sus ejemplos son comparables, la versión async generalmente será un poco más lenta por solicitud, ya que hay más sobrecarga en la máquina de estado que genera el compilador para permitir la funcionalidad async .

Sin embargo, la principal diferencia (y beneficio) es que la versión async permite más solicitudes simultáneas, ya que no bloquea el hilo de procesamiento mientras espera que se complete IO (consulta db, acceso a archivos, solicitud web, etc.).