El problema parece ser que no ha entendido cómo funciona async / await con Entity Framework.
Acerca de Entity Framework
Entonces, veamos este código:
public IQueryable<URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}
y ejemplo de su uso:
repo.GetAllUrls().Where(u => <condition>).Take(10).ToList()
¿Qué pasa ahí?
- Obtenemos el
IQueryable
objeto (aún no accedemos a la base de datos) usandorepo.GetAllUrls()
- Creamos un nuevo
IQueryable
objeto con una condición especificada usando.Where(u => <condition>
- Creamos un nuevo
IQueryable
objeto con límite de paginación especificado usando.Take(10)
- Recuperamos resultados de la base de datos usando
.ToList()
. Nuestro IQueryable
objeto se compila en sql (como select top 10 * from Urls where <condition>
). Y la base de datos puede usar índices, el servidor SQL le envía solo 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:
- Estamos cargando en la memoria todos los mil millones de direcciones URL almacenadas en su base de datos utilizando
await context.Urls.ToListAsync();
.
- Tenemos memoria desbordada. Manera correcta de matar su servidor
Acerca de 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í?
- Comenzando en la línea 1
var stuff1 = ...
- Enviamos una solicitud al servidor SQL para el que queremos obtener algunas cosas1
userId
- Esperamos (el hilo actual está bloqueado)
- Esperamos (el hilo actual está bloqueado)
- .....
- El servidor SQL nos envía una respuesta
- Pasamos a la línea 2
var stuff2 = ...
- Enviamos una solicitud al servidor SQL para el que queremos obtener algunas cosas2.
userId
- Esperamos (el hilo actual está bloqueado)
- Y otra vez
- .....
- El servidor SQL nos envía una respuesta
- Hacemos vista
Así que veamos una versión asincrónica:
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í?
- Enviamos una solicitud al servidor sql para obtener stuff1 (línea 1)
- Enviamos una solicitud al servidor sql para obtener stuff2 (línea 2)
- Esperamos las respuestas del servidor SQL, pero el hilo actual no está bloqueado, puede manejar consultas de otros usuarios
- Hacemos vista
Manera 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 materialized List<Url>
.