Je travaille sur certains éléments de l'API Web avec Entity Framework 6 et l'une de mes méthodes de contrôleur est un "Tout obtenir" qui s'attend à recevoir le contenu d'une table de ma base de données sous la forme IQueryable<Entity>
. Dans mon référentiel, je me demande s’il existe une raison avantageuse de le faire de manière asynchrone, car je suis nouveau dans l’utilisation de EF avec async.
Fondamentalement, cela revient à
public async Task<IQueryable<URL>> GetAllUrlsAsync()
{
var urls = await context.Urls.ToListAsync();
return urls.AsQueryable();
}
contre
public IQueryable<URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}
Est-ce que la version asynchrone apportera réellement des avantages en termes de performances ici ou suis-je sujet à des frais généraux inutiles en projetant d’abord sur une liste (en utilisant async, gardez bien cela) et ALORS sur IQueryable?
Le problème semble être que vous avez mal compris comment async/attend de travailler avec 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à?
IQueryable
(n'accédant pas encore à la base de données) en utilisant repo.GetAllUrls()
IQueryable
avec la condition spécifiée en utilisant .Where(u => <condition>
IQueryable
avec une limite de pagination spécifiée à l'aide de .Take(10)
..ToList()
. Notre objet IQueryable
est compilé en SQL (comme select top 10 * from Urls where <condition>
). Et la base de données peut utiliser des index, SQL Server ne vous envoie que 10 objets (pas tous les milliards d’URL stockés dans la base de données)Ok, 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:
await context.Urls.ToListAsync();
.Pourquoi async/wait 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?
var stuff1 = ...
userId
var stuff2 = ...
userId
Voyons donc une version asynchrone de celle-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?
Donc 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 et de pagination, vous n'avez pas besoin de travailler avec IQueryable
. Vous pouvez simplement utiliser await context.Urls.ToListAsync()
et travailler avec List<Url>
matérialisé.
Il y a une différence énorme dans l'exemple que vous avez posté, la première version:
var urls = await context.Urls.ToListAsync();
Ceci est mauvais, il fait essentiellement select * from table
, renvoie tous les résultats en mémoire et applique ensuite le where
à celui de la collection de mémoire plutôt que de faire select * from table where...
contre la base de données .
La deuxième méthode ne touchera pas la base de données tant qu'une requête n'aura pas été appliquée à l'opération IQueryable
(probablement via une opération de style linq .Where().Select()
qui ne renverra que les valeurs de base de données correspondant à la requête.
Si vos exemples étaient comparables, la version async
sera généralement un peu plus lente à la demande car il y a plus de temps système dans la machine à états générée par le compilateur pour permettre la fonctionnalité async
.
Cependant, la principale différence (et l'avantage) réside dans le fait que la version async
autorise davantage de demandes simultanées, car elle ne bloque pas le processus de traitement en attendant que IO soit terminé (requête de la base de données, accès au fichier). , demande Web, etc.).