O problema parece ser que você não entendeu como async / await funcionam com o Entity Framework.
Sobre Entity Framework
Então, vamos dar uma olhada neste código:
public IQueryable<URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}
e exemplo de seu uso:
repo.GetAllUrls().Where(u => <condition>).Take(10).ToList()
O que acontece lá?
- Estamos obtendo
IQueryable
objeto (não acessando banco de dados ainda) usandorepo.GetAllUrls()
- Nós criamos um novo
IQueryable
objeto com a condição especificada usando.Where(u => <condition>
- Nós criamos um novo
IQueryable
objeto com limite de paginação especificado usando.Take(10)
- Recuperamos os resultados do banco de dados usando
.ToList()
. Nosso IQueryable
objeto é compilado em sql (comoselect top 10 * from Urls where <condition>
). E o banco de dados pode usar índices, o sql server envia a você apenas 10 objetos do seu banco de dados (nem todos os bilhões de urls armazenados no banco de dados)
Ok, vamos dar uma olhada no primeiro código:
public async Task<IQueryable<URL>> GetAllUrlsAsync()
{
var urls = await context.Urls.ToListAsync();
return urls.AsQueryable();
}
Com o mesmo exemplo de uso que obtivemos:
- Estamos carregando na memória todos os bilhões de urls armazenados em seu banco de dados usando
await context.Urls.ToListAsync();
.
- Temos estouro de memória. Maneira certa de matar seu servidor
Sobre async / await
Por que async / await é preferido para usar? Vejamos este código:
var stuff1 = repo.GetStuff1ForUser(userId);
var stuff2 = repo.GetStuff2ForUser(userId);
return View(new Model(stuff1, stuff2));
o que acontece aqui?
- Começando na linha 1
var stuff1 = ...
- Enviamos um pedido ao servidor sql para o qual queremos obter algumas coisas1
userId
- Esperamos (a discussão atual está bloqueada)
- Esperamos (a discussão atual está bloqueada)
- .....
- Servidor Sql enviar para nós resposta
- Vamos para a linha 2
var stuff2 = ...
- Enviamos uma solicitação ao servidor sql para a qual queremos obter algumas coisas2
userId
- Esperamos (a discussão atual está bloqueada)
- E de novo
- .....
- Servidor Sql enviar para nós resposta
- Nós renderizamos a vista
Então, vamos olhar para uma versão assíncrona dele:
var stuff1Task = repo.GetStuff1ForUserAsync(userId);
var stuff2Task = repo.GetStuff2ForUserAsync(userId);
await Task.WhenAll(stuff1Task, stuff2Task);
return View(new Model(stuff1Task.Result, stuff2Task.Result));
o que acontece aqui?
- Enviamos a solicitação ao servidor sql para obter stuff1 (linha 1)
- Enviamos a solicitação ao servidor sql para obter stuff2 (linha 2)
- Esperamos por respostas do servidor sql, mas o thread atual não está bloqueado, ele pode lidar com consultas de outros usuários
- Nós renderizamos a vista
Maneira certa de fazer isso
Bom código aqui:
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();
}
Observe que você deve adicionar using System.Data.Entity
para usar o método ToListAsync()
para IQueryable.
Observe que se você não precisa de filtragem, paginação e outras coisas, não precisa trabalhar com IQueryable
. Você pode apenas usar await context.Urls.ToListAsync()
e trabalhar com materializado List<Url>
.