Retour IEnumerable vs. Iqueriable

Quelle est la différence entre retourner IQueryable<T> et IEnumerable<T> ?

IQueryable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;

IEnumerable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;

Seront tous deux mis à exécution différée et quand doit-on être préférée à une autre?

925
demandé sur Peter Mortensen 2010-05-20 22:13:59

15 réponses

Oui, les deux vous donnera exécution différée .

la différence est que IQueryable<T> est l'interface qui permet LINQ-to-SQL (LINQ.à quelque chose de vraiment. Donc, si vous affinez davantage votre requête sur un IQueryable<T> , cette requête sera exécutée dans la base de données, si possible.

pour le cas IEnumerable<T> , il sera LINQ-to-object, ce qui signifie que tous les objets correspondant à la requête originale devront être chargés en mémoire à partir de la base de données.

dans le code:

IQueryable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);

que le code exécutera SQL pour sélectionner seulement les clients d'or. Le code suivant, d'autre part, exécutera la requête originale dans la base de données, puis filtrera les clients non-or dans la mémoire:

IEnumerable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);

c'est une différence assez importante, et travailler sur IQueryable<T> peut dans de nombreux cas vous éviter de retourner trop de lignes de la base de données. Un autre bon exemple est la pagination: si vous utilisez Take et Skip sur IQueryable , vous n'obtiendrez que le nombre de lignes demandé; si vous faites cela sur un IEnumerable<T> , toutes vos lignes seront chargées en mémoire.

1556
répondu driis 2017-04-18 07:32:15

la réponse du haut est bonne mais elle ne mentionne pas les arbres d'expression qui expliquent" comment " les deux interfaces diffèrent. Fondamentalement, il y a deux ensembles identiques d'extensions LINQ. Where() , Sum() , Count() , FirstOrDefault() , etc tous ont deux versions: l'une qui accepte les fonctions et l'autre qui accepte les expressions.

  • la signature de la version IEnumerable Where(Func<Customer, bool> predicate)

  • la signature de la version IQueryable est: Where(Expression<Func<Customer, bool>> predicate)

vous avez probablement utilisé les deux sans vous en rendre compte parce que les deux sont appelés en utilisant une syntaxe identique:

p.ex. Where(x => x.City == "<City>") fonctionne à la fois sur IEnumerable et IQueryable

  • Lorsqu'on utilise Where() sur une collection IEnumerable , le compilateur passe une fonction compilée à Where()

  • en utilisant Where() sur une collection IQueryable , le compilateur passe un arbre d'expression à Where() . Un arbre d'expression est comme le système de réflexion mais pour le code. Le compilateur convertit votre code en une structure de données qui décrit ce que votre code dans un format facile à digérer.

pourquoi s'embêter avec cette expression arbre chose? Je veux juste Where() pour filtrer mes données. La raison principale est que les ORMs EF et Linq2SQL peuvent convertir directement les arbres d'expression en SQL où votre code s'exécute beaucoup plus rapidement.

Oh, ça ressemble à un boost gratuit, devrais-je utiliser AsQueryable() partout dans ce cas? Non, IQueryable n'est utile que si le fournisseur de données sous-jacent peut en faire quelque chose. Convertir quelque chose comme un List régulier en IQueryable ne vous donnera aucun avantage.

212
répondu Jacob 2017-04-18 07:40:55

Oui, les deux utilisent l'exécution différée. Illustrons la différence en utilisant le profileur de serveur SQL....

quand on exécute le code suivant:

MarketDevEntities db = new MarketDevEntities();

IEnumerable<WebLog> first = db.WebLogs;
var second = first.Where(c => c.DurationSeconds > 10);
var third = second.Where(c => c.WebLogID > 100);
var result = third.Where(c => c.EmailAddress.Length > 11);

Console.Write(result.First().UserName);

dans SQL Server profiler nous trouvons une commande égale à:

"SELECT * FROM [dbo].[WebLog]"

il faut environ 90 secondes pour exécuter ce bloc de code contre une table WebLog qui a 1 million d'enregistrements.

ainsi, tous les enregistrements de table sont chargés dans la mémoire comme les objets, puis avec chaque .Où () ce sera un autre filtre en mémoire contre ces objets.

lorsque nous utilisons IQueryable au lieu de IEnumerable dans l'exemple ci-dessus (deuxième ligne):

dans SQL Server profiler nous trouvons une commande égale à:

"SELECT TOP 1 * FROM [dbo].[WebLog] WHERE [DurationSeconds] > 10 AND [WebLogID] > 100 AND LEN([EmailAddress]) > 11"

il faut environ quatre secondes pour exécuter ce bloc de code en utilisant IQueryable .

IQueryable a une propriété appelée Expression qui stocke une expression d'arbre qui commence à être créée lorsque nous avons utilisé le result dans notre exemple (qui est appelé exécution différée), et à la fin cette expression sera convertie en une requête SQL pour tourner sur le moteur de base de données.

62
répondu Kasper Roma 2016-09-14 22:13:51

les deux vous donneront une exécution différée, Oui.

pour ce qui est de savoir lequel est préféré à l'autre, cela dépend de la source de données sous-jacente.

retourner un IEnumerable forcera automatiquement le runtime à utiliser LINQ pour interroger les objets de votre collection.

retourner un IQueryable (qui implémente IEnumerable, soit dit en passant) fournit la fonctionnalité supplémentaire pour traduire votre requête en quelque chose qui pourrait perform better on the underlying source (LINQ to SQL, LINQ to XML, etc.).

54
répondu Justin Niessner 2014-09-25 22:55:08

en général, vous voulez préserver le type statique original de la requête jusqu'à ce qu'il importe.

pour cette raison, vous pouvez définir votre variable comme" var "au lieu de IQueryable<> ou IEnumerable<> et vous saurez que vous ne changez pas le type.

si vous commencez avec un IQueryable<> , vous voulez généralement le garder comme un IQueryable<> jusqu'à ce qu'il ya une raison impérieuse de le changer. La raison pour cela est que vous voulez pour donner au processeur de requête autant d'informations que possible. Par exemple, si vous n'allez utiliser que 10 résultats (vous avez appelé Take(10) ), alors vous voulez que SQL Server le sache afin qu'il puisse optimiser ses plans de requête et vous envoyer seulement les données que vous utiliserez.

une raison impérieuse pour changer le type de IQueryable<> à IEnumerable<> pourrait être que vous appelez une fonction d'extension que la mise en œuvre de IQueryable<> dans votre objet particulier soit ne peut pas manipuler ou manipule de manière inefficace. Dans ce cas, vous pouvez convertir le type en IEnumerable<> (en assignant à une variable de type IEnumerable<> ou en utilisant la méthode d'extension AsEnumerable par exemple) de sorte que les fonctions d'extension que vous appelez finissent par être celles de la classe Enumerable au lieu de la classe Queryable .

23
répondu AJS 2012-05-15 13:27:47

en termes généraux, je recommande ce qui suit:

pour retourner IQueryable si vous voulez activer le développeur en utilisant votre méthode pour affiner la requête que vous retournez avant l'exécution.

si vous voulez transporter juste un ensemble d'objets à énumérer, il suffit de prendre IEnumerable.

Imaginez un IQueryable comme ce qu'il est, une "requête" pour des données (que vous pouvez affiner si vous voulez)

Un IEnumerable est un ensemble d'objets (qui a déjà été reçu ou a été créé) sur lesquels vous pouvez énumérer.

22
répondu sebastianmehler 2016-11-26 16:41:09

beaucoup de choses ont été dites auparavant, mais de retour aux sources, d'une manière plus technique:

  1. IEnumerable est une collection d'objets en mémoire que vous pouvez énumérer - une séquence en mémoire qui permet d'itérer à travers (rend la manière facile pour dans foreach boucle, bien que vous pouvez aller avec IEnumerator seulement). Ils résident dans la mémoire.
  2. IQueryable est un expression arbre qui se traduira en quelque chose d'autre à un moment donné avec la capacité d'énumérer sur le résultat final . Je suppose que c'est ce qui trouble la plupart des gens.

ils ont évidemment des connotations différentes.

IQueryable représente un arbre d'expressions (une requête, simplement) qui sera traduit en quelque chose d'autre par le fournisseur de requête sous-jacent dès que les API de publication sont appelées, comme LINQ fonctions agrégées(somme, compte, etc.) ou ToList[Tableau, Dictionnaire...]. Et IQueryable les objets implémentent aussi IEnumerable , IEnumerable<T> de sorte que s'ils représentent une requête le résultat de cette requête pourrait être itéré. Cela signifie que IQueryable ne doit pas être seulement des requêtes. Le bon terme est qu'ils sont arbres d'expression .

maintenant comment ces expressions sont exécutées et ce qu'elles tournent to est tout à fait jusqu'à soi-disant fournisseurs de requête (exécuteurs d'expression Nous pouvons les penser).

Dans le Entity Framework monde (qui est mystique de données sous-jacente source prestataire ou fournisseur de requêtes) IQueryable expressions sont traduites en natif T-SQL requêtes. Nhibernate fait des choses similaires avec eux. Vous pouvez écrire votre propre en suivant les concepts assez bien décrits dans LINQ: construction un fournisseur IQueryable lien, par exemple, et vous pourriez vouloir avoir une API de recherche personnalisée pour votre service de fournisseur de magasin de produits.

donc fondamentalement, IQueryable les objets sont construits tout le long jusqu'à ce que nous les relâchions explicitement et que nous disions au système de les réécrire en SQL ou autre et d'envoyer la chaîne d'exécution pour le traitement ultérieur.

comme si différé exécution c'est une fonctionnalité LINQ pour maintenir l'arborescence de l'expression dans la mémoire et l'envoyer dans l'exécution seulement sur demande, chaque fois que certains API sont appelées contre la séquence (le même nombre, Toliste, etc.).

l'utilisation correcte des deux dépend fortement des tâches que vous faites face pour le cas spécifique. Pour le modèle de dépôt bien connu j'opte personnellement pour retourner IList , c'est-à-dire IEnumerable au-dessus des listes (indexeurs et comme.) C'est donc mon conseil d'utiliser IQueryable uniquement dans les dépôts et IEnumerable ailleurs dans le code. Ne pas dire au sujet de la testabilité préoccupations que IQueryable se décompose et ruine la séparation des préoccupations Principe. Si vous renvoyez une expression de l'intérieur des référentiels, les consommateurs peuvent jouer avec la couche de persistance comme ils le souhaiteraient.

un petit ajout au mess :) (d'une discussion dans les commentaires)) Aucun d'entre eux sont les objets en mémoire puisqu'ils ne sont pas de vrais types en soi, ils sont des marqueurs d'un type - si vous voulez aller aussi profond. Mais il est logique (et c'est pourquoi même MSDN l'a dit ainsi) de penser aux IEnumerables comme des collections de mémoire tandis que les IQueryables comme des arbres d'expression. Le point est que l'interface IQueryable hérite de l'interface IEnumerable de sorte que si elle représente une requête, les résultats de cette requête peuvent être énumérés. L'énumération provoque l'arbre d'expression associé à un objet IQueryable à exécuter. Donc, en fait, on ne peut pas vraiment appeler un membre innombrables sans avoir l'objet dans la mémoire. Il entrera si vous le faites, de toute façon, s'il n'est pas vide. IQueryables ne sont que des requêtes, pas les données.

18
répondu Arman McHitarian 2016-09-14 22:10:32

il y a un billet de blog avec un bref exemple de code source sur la façon dont l'utilisation abusive de IEnumerable<T> peut avoir un impact dramatique sur les performances de requête LINQ: Entity Framework: IQueryable vs. IEnumerable .

si nous creusons plus profondément et regardons dans les sources, nous pouvons voir qu'Il ya évidemment différentes méthodes d'extension sont mis au point pour IEnumerable<T> :

// Type: System.Linq.Enumerable
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll
public static class Enumerable
{
    public static IEnumerable<TSource> Where<TSource>(
        this IEnumerable<TSource> source, 
        Func<TSource, bool> predicate)
    {
        return (IEnumerable<TSource>) 
            new Enumerable.WhereEnumerableIterator<TSource>(source, predicate);
    }
}

et IQueryable<T> :

// Type: System.Linq.Queryable
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll
public static class Queryable
{
    public static IQueryable<TSource> Where<TSource>(
        this IQueryable<TSource> source, 
        Expression<Func<TSource, bool>> predicate)
    {
        return source.Provider.CreateQuery<TSource>(
            Expression.Call(
                null, 
                ((MethodInfo) MethodBase.GetCurrentMethod()).MakeGenericMethod(
                    new Type[] { typeof(TSource) }), 
                    new Expression[] 
                        { source.Expression, Expression.Quote(predicate) }));
    }
}

le premier l'un renvoie un itérateur énumérable, et le second crée une requête par l'intermédiaire du fournisseur de requête, spécifié dans la source IQueryable .

17
répondu Olexander 2013-04-23 22:45:22

j'ai récemment rencontré un problème avec IEnumrable v. IQueryable. L'algorithme utilisé a d'abord effectué une requête IQueryable pour obtenir un ensemble de résultats. Ils ont ensuite été transmis à une boucle foreach, les éléments étant instanciés dans une classe EF. Cette classe EF a ensuite été utilisée dans la clause from D'une requête Linq à Entity, ce qui a rendu le résultat IEnumerable. Je suis assez nouveau à EF et Linq pour les entités, donc il a fallu un certain temps pour comprendre ce qu'était le goulot d'étranglement. En utilisant MiniProfiling, j'ai trouvé le requête puis converti toutes les opérations individuelles en une seule Linq IQueryable Pour requête Entities. L'IEnumerable a pris 15 secondes et l'Iqueriable 0,5 secondes à exécuter. Il y avait trois tables impliquées et, après avoir lu ceci, je crois que la requête IEnumerable formait en fait un produit croisé de trois tables et filtrait les résultats.

essayez D'utiliser IQueryables comme règle de base et profilez votre travail pour rendre vos changements mesurables.

9
répondu sscheider 2012-07-13 07:17:08

je voudrais clarifier quelques points en raison de réponses apparemment contradictoires (surtout autour de IEnumerable).

(1) IQueryable étend l'interface IEnumerable . (Vous pouvez envoyer un IQueryable à quelque chose qui attend IEnumerable sans erreur.)

(2) IQueryable et IEnumerable LINQ tentative de chargement différé lors d'une itération sur l'ensemble des résultats. (Notez que la mise en œuvre peut être vue dans les méthodes d'extension d'interface pour chaque type.)

En d'autres termes, IEnumerables ne sont pas exclusivement "en mémoire". IQueryables ne sont pas toujours exécutés dans la base de données. IEnumerable doit charger des choses dans la mémoire (une fois récupéré, peut-être paresseusement) parce qu'il n'a pas de fournisseur de données abstraites. IQueryables s'appuie sur un fournisseur Abstrait (comme LINQ-to-SQL), bien que cela puisse aussi être le fournisseur .NET in-memory.

Exemple de cas d'utilisation

(a) extraire la liste des enregistrements comme IQueryable du contexte EF. (Aucun enregistrement n'est en mémoire.)

B) passer le IQueryable à une vue dont le modèle est IEnumerable . (Valide. IQueryable s'étend IEnumerable .)

(c) parcourir et d'accéder aux données de l'ensemble des dossiers, de l'enfant, les entités et les propriétés de la vue. (Peut causer des exceptions!)

Problèmes Possibles

(1) Le IEnumerable tente le chargement paresseux et votre contexte de données est expiré. Exception lancée parce que le fournisseur n'est plus disponible.

(2) Cadre de l'Entité entité procurations sont activé (par défaut), et que vous tentez d'accéder à une relative (virtuel) de l'objet à l'expiration d'un contexte de données. Même que (1).

(3) fois (MARS). Si vous itérez sur le IEnumerable dans un bloc foreach( var record in resultSet ) et simultanément tentative d'accéder à record.childEntity.childProperty , vous pouvez vous retrouver avec MARS en raison de chargement paresseux à la fois de l'ensemble de données et de l'entité relationnelle. Cela va provoquer une exception si elle n'est pas activée dans votre chaîne de connexion.

Solution

  • j'ai trouvé qu'activer MARS dans la chaîne de connexion ne fonctionne pas de manière fiable. Je vous suggère d'éviter MARS à moins qu'elle ne soit bien comprise et explicitement désirée.

exécutez la requête et stockez les résultats en invoquant resultList = resultSet.ToList() cela semble être le moyen le plus simple de s'assurer que vos entités sont en mémoire.

dans les cas où vous accédez à des entités liées, Vous pouvez toujours avoir besoin d'un contexte de données. Soit cela, soit vous pouvez désactiver entités mandataires et explicitement Include entités liées à votre DbSet .

9
répondu Alexander Pritchard 2015-02-09 19:13:16

la principale différence entre" IEnumerable "et" IQueryable " concerne l'exécution de la logique du filtre. L'un exécute du côté client (en mémoire) et l'autre exécute sur la base de données.

par exemple, nous pouvons considérer un exemple où nous avons 10.000 enregistrements pour un utilisateur dans notre base de données et disons seulement 900 sur qui sont des utilisateurs actifs, donc dans ce cas si nous utilisons "IEnumerable" alors d'abord il charge les 10.000 enregistrements dans la mémoire et applique ensuite L'IsActive filtre sur elle qui renvoie éventuellement les 900 utilisateurs actifs.

alors que d'un autre côté sur le même cas si nous utilisons "IQueryable" il appliquera directement le filtre IsActive sur la base de données qui directement de là retournera les 900 utilisateurs actifs.

Référence Link

7
répondu Tabish Usman 2016-11-25 05:29:48

ce sont des différences entre IQueryable<T> et IEnumerable<T>

difference between returning IQueryable<T vs. IEnumerable<T

6
répondu Basheer AL-MOMANI 2016-10-13 05:34:18

nous pouvons utiliser les deux de la même façon, et ils sont seulement différents dans la performance.

IQueryable exécute seulement contre la base de données d'une manière efficace. Cela signifie qu'il crée une requête select entière et obtient seulement les enregistrements liés.

par exemple, nous voulons prendre le top 10 clients dont le nom commence par "Nimal". Dans ce cas, la requête select sera générée comme select top 10 * from Customer where name like ‘Nimal%’ .

mais si nous utilisions IEnumerable, la requête serait comme select * from Customer where name like ‘Nimal%’ et les dix premiers seront filtrés au niveau de codage C# (il obtient tous les enregistrements de client de la base de données et les passe dans C#).

4
répondu user3710357 2016-09-14 22:17:06

En plus des 2 premières bonnes réponses (par driis & par Jacob) :

IEnumerable l'interface est dans le Système.Espace de noms des Collections.

l'objet IEnumerable représente un ensemble de données en mémoire et ne peut avancer que sur ces données. La requête représentée par l'objet IEnumerable est exécutée immédiatement et complètement, de sorte que l'application reçoit des données rapidement.

lorsque le la requête est exécutée, IEnumerable charge toutes les données, et si nous avons besoin de filtre, le filtrage se fait sur le côté client.

interface IQueryable est situé dans le système.Espace de noms Linq.

l'objet IQueryable fournit un accès à distance à la base de données et vous permet de naviguer à travers les données soit dans un ordre direct du début à la fin, ou dans l'ordre inverse. Dans le processus de création d'une requête, la objet retourné est IQueryable, la requête est optimisée. En conséquence, moins de mémoire est consommée pendant son exécution, moins de bande passante réseau, mais en même temps il peut être traité un peu plus lentement qu'une requête qui renvoie un objet IEnumerable.

que choisir?

si vous avez besoin de l'ensemble des données retournées, alors il est préférable d'utiliser IEnumerable, qui fournit la vitesse maximale.

si vous n'avez pas besoin de l'ensemble des données retournées, mais seulement quelques données filtrées, alors il est préférable d'utiliser IQueryable.

4
répondu Gleb B 2017-05-22 10:29:22

tout en utilisant LINQ pour les entités, il est important de comprendre quand pour utiliser IEnumerable et IQueryable. Si nous utilisons IEnumerable, la requête sera exécutée immédiatement. Si nous utilisons IQueryable, le l'exécution de la requête sera reportée jusqu'à ce que l'application demande énumération. Voyons maintenant ce qu'il faut prendre en considération pour décider s'il faut utilisez IQueryable ou IEnumerable. Utiliser IQueryable vous donne une chance de créer une requête LINQ complexe en utilisant des déclarations multiples sans l'exécution de la requête à la base de données. La requête est exécuté seulement lorsque la requête LINQ finale est énumérée.

-4
répondu Orel Hernandez 2015-07-10 16:34:22