Je travaille sur quelques trucs d'API Web en utilisant Entity Framework 6 et l'une de mes méthodes de contrôleur est un "Get All" qui s'attend à recevoir le contenu d'une table de ma base de données en tant que 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 d'EF avec async.
Fondamentalement, cela se résume à
public async Task<IQueryable<URL>> GetAllUrlsAsync()
{
var urls = await context.Urls.ToListAsync();
return urls.AsQueryable();
}
contre
public IQueryable<URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}
La version asynchrone apportera-t-elle réellement des avantages en termes de performances ici ou est-ce que je subis une surcharge inutile en projetant d'abord sur une liste (en utilisant async, vous l'esprit) et ALORS en allant à IQueryable?
c#
entity-framework
async-await
Jesse Carter
la source
la source
Réponses:
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:
et exemple d'utilisation:
Que se passe-t-il là?
IQueryable
objet (pas encore accès à la base de données) en utilisantrepo.GetAllUrls()
IQueryable
objet avec une condition spécifiée en utilisant.Where(u => <condition>
IQueryable
objet avec une limite de pagination spécifiée en utilisant.Take(10)
.ToList()
. NotreIQueryable
objet est compilé en sql (likeselect 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:
Avec le même exemple d'utilisation, nous avons obtenu:
await context.Urls.ToListAsync();
.À propos de async / await
Pourquoi async / await est-il préférable d'utiliser? Regardons ce code:
Que se passe t-il ici?
var stuff1 = ...
userId
var stuff2 = ...
userId
Regardons donc une version asynchrone de celui-ci:
Que se passe t-il ici?
Bonne façon de le faire
Tellement bon code ici:
Notez que vous devez ajouter
using System.Data.Entity
pour utiliser la méthodeToListAsync()
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 utiliserawait context.Urls.ToListAsync()
et travailler avec matérialiséList<Url>
.la source
GetAllUrlsByUser
méthode, vous n'avez pas besoin de le rendre asynchrone. Renvoyez simplement la tâche et évitez que la machine à états inutile ne soit générée par le compilateur.async
etawait
si vous ne faites rien avec la liste. Laissez l'appelantawait
. Lorsque vous attendez l'appel à ce stade,return await GetAllUrls().Where(u => u.User.Id == userId).ToListAsync();
vous créez un wrapper asynchrone supplémentaire lorsque vous décompilez l'assembly et examinez l'IL.Il y a une énorme différence dans l'exemple que vous avez posté, la première version:
Ceci est mauvais , il le fait essentiellement
select * from table
, renvoie tous les résultats en mémoire, puis applique lewhere
contre cela dans la collection de mémoire plutôt que de le faireselect * from table where...
contre la base de données.La deuxième méthode n'atteindra pas réellement la base de données tant qu'une requête ne sera pas appliquée au
IQueryable
(probablement via une.Where().Select()
opération de style linq qui ne retournera que les valeurs de base de données correspondant à la requête.Si vos exemples étaient comparables, la
async
version sera généralement légèrement plus lente par requête car il y a plus de surcharge dans la machine à états que le compilateur génère pour autoriser laasync
fonctionnalité.Cependant, la principale différence (et avantage) est que la
async
version autorise plus de requêtes simultanées car elle ne bloque pas le thread de traitement pendant qu'elle attend la fin des E / S (requête de base de données, accès au fichier, requête Web, etc.).la source
En bref,
IQueryable
est conçu pour reporter le processus RUN et d'abord construire l'expression en conjonction avec d'autresIQueryable
expressions, puis interpréter et exécuter l'expression dans son ensemble.Mais la
ToList()
méthode (ou quelques sortes de méthodes comme celle-là), consiste à exécuter l'expression instantanément "telle quelle".Votre première méthode (
GetAllUrlsAsync
) s'exécutera immédiatement, car elle estIQueryable
suivie deToListAsync()
method. par conséquent, il s'exécute instantanément (asynchrone) et renvoie un tas deIEnumerable
s.En attendant, votre deuxième méthode (
GetAllUrls
) ne sera pas exécutée. Au lieu de cela, il renvoie une expression et CALLER de cette méthode est chargé d'exécuter l'expression.la source