Beaucoup a été dit précédemment, mais revenons aux racines, de manière plus technique:
IEnumerable
est une collection d'objets en mémoire que vous pouvez énumérer - une séquence en mémoire qui permet de parcourir (facilite la foreach
boucle intra , bien que vous ne puissiez y aller qu'avec IEnumerator
). Ils résident dans la mémoire telle quelle.
IQueryable
est un arbre d'expression qui sera traduit en quelque chose d'autre à un moment donné avec la possibilité d'énumérer le résultat final . Je suppose que c'est ce qui déroute la plupart des gens.
Ils ont évidemment des connotations différentes.
IQueryable
représente un arbre d'expression (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 les fonctions d'agrégation LINQ (Sum, Count, etc.) ou ToList [Array, Dictionary ,. ..]. Et les IQueryable
objets implémentent également IEnumerable
, de IEnumerable<T>
sorte que s'ils représentent une requête, le résultat de cette requête puisse être itéré. Cela signifie que IQueryable ne doit pas être uniquement des requêtes. Le bon terme est qu'ils sont des arbres d'expression .
Maintenant, la façon dont ces expressions sont exécutées et vers quoi elles se tournent appartient aux soi-disant fournisseurs de requêtes (exécuteurs d'expressions auxquels nous pouvons penser).
Dans le monde Entity Framework (qui est ce fournisseur de source de données sous-jacent mystique, ou le fournisseur de requêtes), les IQueryable
expressions sont traduites en requêtes T-SQL natives . Nhibernate
fait des choses similaires avec eux. Vous pouvez écrire le vôtre en suivant les concepts assez bien décrits dans LINQ: création d'un lien de fournisseur IQueryable , par exemple, et vous souhaiterez peut-être avoir une API de requête personnalisée pour votre service de fournisseur de magasin de produits.
Donc, fondamentalement, les IQueryable
objets sont construits tout le temps jusqu'à ce que nous les libérions explicitement et disions au système de les réécrire dans SQL ou autre et d'envoyer la chaîne d'exécution pour un traitement ultérieur.
Comme pour une exécution différée, il est possible LINQ
de conserver le schéma d'arborescence d'expressions dans la mémoire et de l'envoyer dans l'exécution uniquement à la demande, chaque fois que certaines API sont appelées par rapport à la séquence (les mêmes Count, ToList, etc.).
La bonne utilisation des deux dépend fortement des tâches auxquelles vous êtes confronté dans le cas spécifique. Pour le modèle de référentiel bien connu, j'opte personnellement pour le retour IList
, c'est-à-dire IEnumerable
sur les listes (indexeurs et similaires). Il est donc mon conseil de n'utiliser IQueryable
que dans les référentiels et IEnumerable n'importe où ailleurs dans le code. Ne pas parler des problèmes de testabilité qui IQueryable
décomposent et ruinent le principe de séparation des préoccupations . Si vous renvoyez une expression à partir de référentiels, les consommateurs peuvent jouer avec la couche de persistance comme ils le souhaitent.
Un petit ajout au gâchis :) (d'après une discussion dans les commentaires)) Aucun d'entre eux n'est un objet en mémoire car ce ne sont pas de vrais types en soi, ce sont des marqueurs d'un type - si vous voulez aller aussi loin. Mais il est logique (et c'est pourquoi même MSDN le dit ainsi) de considérer IEnumerables comme des collections en mémoire tandis que IQueryables comme des arbres d'expression. Le fait 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 entraîne l'exécution de l'arborescence d'expression associée à un objet IQueryable. Donc, en fait, vous ne pouvez pas vraiment appeler un membre IEnumerable sans avoir l'objet en mémoire. Il y entrera si vous le faites, de toute façon, s'il n'est pas vide. Les IQueryables ne sont que des requêtes, pas les données.