Il problema sembra essere che hai frainteso il funzionamento di async / await con Entity Framework.
Informazioni su Entity Framework
Quindi, diamo un'occhiata a questo codice:
public IQueryable<URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}
ed esempio di utilizzo:
repo.GetAllUrls().Where(u => <condition>).Take(10).ToList()
Cosa succede lì?
- Stiamo ottenendo
IQueryable
oggetto (non ancora accedendo al database) utilizzandorepo.GetAllUrls()
- Creiamo un nuovo
IQueryable
oggetto con la condizione specificata usando.Where(u => <condition>
- Creiamo un nuovo
IQueryable
oggetto con il limite di paging specificato utilizzando.Take(10)
- Recuperiamo i risultati dal database utilizzando
.ToList()
. Il nostro IQueryable
oggetto è compilato in sql (like select top 10 * from Urls where <condition>
). E il database può utilizzare gli indici, il server sql ti invia solo 10 oggetti dal tuo database (non tutti i miliardi di URL memorizzati nel database)
Ok, diamo un'occhiata al primo codice:
public async Task<IQueryable<URL>> GetAllUrlsAsync()
{
var urls = await context.Urls.ToListAsync();
return urls.AsQueryable();
}
Con lo stesso esempio di utilizzo abbiamo ottenuto:
- Stiamo caricando in memoria tutti i miliardi di URL memorizzati nel tuo database utilizzando
await context.Urls.ToListAsync();
.
- Abbiamo un overflow di memoria. Il modo giusto per uccidere il tuo server
Informazioni su async / await
Perché è preferibile utilizzare async / await? Diamo un'occhiata a questo codice:
var stuff1 = repo.GetStuff1ForUser(userId);
var stuff2 = repo.GetStuff2ForUser(userId);
return View(new Model(stuff1, stuff2));
Che succede qui?
- A partire dalla linea 1
var stuff1 = ...
- Inviamo la richiesta al server sql per cui vogliamo ottenere qualcosa1
userId
- Aspettiamo (il thread corrente è bloccato)
- Aspettiamo (il thread corrente è bloccato)
- .....
- Il server SQL inviaci la risposta
- Passiamo alla riga 2
var stuff2 = ...
- Inviamo una richiesta al server sql per cui vogliamo ottenere qualcosa2
userId
- Aspettiamo (il thread corrente è bloccato)
- E di nuovo
- .....
- Il server SQL inviaci la risposta
- Rendiamo la vista
Quindi diamo un'occhiata a una versione asincrona di esso:
var stuff1Task = repo.GetStuff1ForUserAsync(userId);
var stuff2Task = repo.GetStuff2ForUserAsync(userId);
await Task.WhenAll(stuff1Task, stuff2Task);
return View(new Model(stuff1Task.Result, stuff2Task.Result));
Che succede qui?
- Inviamo una richiesta al server sql per ottenere stuff1 (riga 1)
- Inviamo una richiesta al server sql per ottenere stuff2 (riga 2)
- Attendiamo risposte dal server sql, ma il thread corrente non è bloccato, può gestire le query di altri utenti
- Rendiamo la vista
Modo giusto per farlo
Quindi buon codice qui:
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();
}
Nota che devi aggiungere using System.Data.Entity
per utilizzare il metodo ToListAsync()
per IQueryable.
Nota che se non hai bisogno di filtri, paging e cose del genere, non devi lavorare con IQueryable
. Puoi semplicemente usare await context.Urls.ToListAsync()
e lavorare con materialized List<Url>
.