Le problème semble être que vous avez mal compris comment async / await fonctionne avec Entity Framework.
À propos d'Entity Framework
Alors, regardons ce code:
public IQueryable<URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}
et exemple d'utilisation:
repo.GetAllUrls().Where(u => <condition>).Take(10).ToList()
Que se passe-t-il là?
- Nous obtenons un
IQueryable
objet (pas encore accès à la base de données) en utilisantrepo.GetAllUrls()
- Nous créons un nouvel
IQueryable
objet avec une condition spécifiée en utilisant.Where(u => <condition>
- Nous créons un nouvel
IQueryable
objet avec une limite de pagination spécifiée en utilisant.Take(10)
- Nous récupérons les résultats de la base de données en utilisant
.ToList()
. Notre IQueryable
objet est compilé en sql (like select top 10 * from Urls where <condition>
). Et la base de données peut utiliser des index, le serveur sql ne vous envoie que 10 objets de votre base de données (pas tous les milliards d'urls stockés dans la base de données)
Bon, regardons le premier code:
public async Task<IQueryable<URL>> GetAllUrlsAsync()
{
var urls = await context.Urls.ToListAsync();
return urls.AsQueryable();
}
Avec le même exemple d'utilisation, nous avons obtenu:
- Nous chargeons en mémoire tous les milliards d'urls stockés dans votre base de données en utilisant
await context.Urls.ToListAsync();
.
- Nous avons un débordement de mémoire. Bonne façon de tuer votre serveur
À propos de async / await
Pourquoi async / await est-il préférable d'utiliser? Regardons ce code:
var stuff1 = repo.GetStuff1ForUser(userId);
var stuff2 = repo.GetStuff2ForUser(userId);
return View(new Model(stuff1, stuff2));
Que se passe t-il ici?
- À partir de la ligne 1
var stuff1 = ...
- Nous envoyons une requête au serveur SQL pour laquelle nous voulons obtenir des informations
userId
- On attend (le thread actuel est bloqué)
- On attend (le thread actuel est bloqué)
- .....
- Le serveur SQL nous envoie une réponse
- Nous passons à la ligne 2
var stuff2 = ...
- Nous envoyons une requête au serveur SQL pour laquelle nous voulons obtenir des trucs2
userId
- On attend (le thread actuel est bloqué)
- Et encore
- .....
- Le serveur SQL nous envoie une réponse
- Nous rendons la vue
Regardons donc une version asynchrone de celui-ci:
var stuff1Task = repo.GetStuff1ForUserAsync(userId);
var stuff2Task = repo.GetStuff2ForUserAsync(userId);
await Task.WhenAll(stuff1Task, stuff2Task);
return View(new Model(stuff1Task.Result, stuff2Task.Result));
Que se passe t-il ici?
- Nous envoyons une demande au serveur SQL pour obtenir stuff1 (ligne 1)
- Nous envoyons une demande au serveur SQL pour obtenir stuff2 (ligne 2)
- Nous attendons les réponses du serveur sql, mais le thread actuel n'est pas bloqué, il peut gérer les requêtes d'un autre utilisateur
- Nous rendons la vue
Bonne façon de le faire
Tellement bon code ici:
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();
}
Notez que vous devez ajouter using System.Data.Entity
pour utiliser la méthode ToListAsync()
pour IQueryable.
Notez que si vous n'avez pas besoin de filtrage, de pagination et d'autres choses, vous n'avez pas besoin de travailler avec IQueryable
. Vous pouvez simplement utiliser await context.Urls.ToListAsync()
et travailler avec matérialisé List<Url>
.