IQueryable<T>
est destiné à permettre à un fournisseur de requêtes (par exemple, un ORM comme LINQ to SQL ou Entity Framework) d'utiliser les expressions contenues dans une requête pour traduire la demande dans un autre format. En d'autres termes, LINQ-to-SQL regarde les propriétés sur les entités que vous utilisez avec les comparaisons que vous faites et crée en fait une instruction SQL express (espérons-le) d'un montant équivalent de la demande.
IEnumerable<T>
est plus générique que l' IQueryable<T>
(bien que toutes les instances de l' IQueryable<T>
œuvre IEnumerable<T>
) et seulement définit une séquence. Cependant, il existe des méthodes d'extension disponibles au sein de l' Enumerable
classe qui définissent des requêtes de type opérateurs de l'interface et l'utilisation du code ordinaire pour l'évaluation de ces conditions.
List<T>
est juste un format de sortie, et alors qu'il implémente IEnumerable<T>
, n'est pas directement liée à l'interrogation.
En d'autres termes, lorsque vous utilisez IQueryable<T>
, vous êtes à la définition et à l'expression qui se traduit en quelque chose d'autre. Même si vous écrivez du code, ce code n'est jamais exécutée, il ne s' inspecter et de les transformer en quelque chose d'autre, comme un réel requête SQL. De ce fait, que certaines choses ne sont valables que dans ces expressions. Par exemple, vous ne pouvez pas appeler une fonction ordinaire que vous définissez à partir de ces expressions, depuis LINQ-to-SQL ne sais pas comment transformer votre appel dans une instruction SQL. La plupart de ces restrictions ne sont évaluées au moment de l'exécution, malheureusement.
Lorsque vous utilisez IEnumerable<T>
pour l'interrogation, vous êtes à l'aide de LINQ-to-Objets, ce qui signifie que vous écrivez le code qui est utilisé pour l'évaluation de votre requête ou de transformer les résultats, il y a donc, en général, pas de restrictions sur ce que vous pouvez faire. Vous pouvez appeler d'autres fonctions au sein de ces expressions librement.
Avec LINQ to SQL
Va de pair avec la distinction ci-dessus, il est également important de garder à l'esprit la façon dont cela fonctionne dans la pratique. Lorsque vous écrivez une requête à l'encontre d'une classe de contexte de données dans LINQ to SQL, il produit un IQueryable<T>
. Tout ce que vous faites à l'encontre de l' IQueryable<T>
lui-même va se transformer en SQL, donc votre filtrage et transformation sera fait sur le serveur. Tout ce que vous faites à l'encontre de ce que l' IEnumerable<T>
, sera fait au niveau de l'application. Parfois, cela est souhaitable (dans le cas où vous avez besoin de faire usage de code côté client, par exemple), mais dans de nombreux cas, ce n'est pas intentionnelle.
Par exemple, si j'avais un contexte avec un Customers
propriété représentant une Customer
tableau, et chaque client a un CustomerId
colonne, regardons deux façons de le faire cette requête:
var query = (from c in db.Customers where c.CustomerId == 5 select c).First();
Ceci va produire SQL qui interroge la base de données pour l' Customer
record avec un CustomerId
égal à 5. Quelque chose comme:
select CustomerId, FirstName, LastName from Customer where CustomerId = 5
Maintenant, ce qui se passe si nous nous tournons Customers
en IEnumerable<Customer>
par l'aide de la AsEnumerable()
méthode d'extension?
var query = (from c in db.Customers.AsEnumerable() where c.CustomerId == 5 select c).First();
Ce simple changement a une conséquence grave. Puisque nous tournons Customers
en IEnumerable<Customer>
, cela apportera toute la table et le filtre sur le côté client (bien, strictement parlant, cela va ramener chaque ligne de la table jusqu'à ce qu'il rencontre celle qui s'adapte les critères du programme, mais le point est le même).
ToList()
Jusqu'à présent, nous avons seulement parlé IQueryable
et IEnumerable
. C'est parce qu'ils sont semblables, des interfaces. Dans les deux cas, vous êtes à la définition d'une requête; qui est, vous êtes définir où trouver les données, ce que les filtres à appliquer, et que les données de retour. Ces deux sont des requêtes
query = from c in db.Customers where c.CustomerId == 5 select c;
query = from c in db.Customers.AsEnumerable() where c.CustomerId == 5 select c;
Comme nous en avons parlé, la première requête est à l'aide de IQueryable
et la seconde utilise IEnumerable
. Dans les deux cas, cependant, c'est juste une requête. La définition de la requête n'est pas réellement rien faire contre la source de données. La requête est exécutée lorsque le code commence à itérer sur la liste. Cela peut se produire de plusieurs façons; un foreach
boucle, appelant ToList()
, etc.
La requête est exécutée la première et à chaque fois c'est itérée. Si vous vous appelez ToList()
sur query
deux fois, vous vous retrouvez avec deux listes avec des objets distincts. Ils peuvent contenir les mêmes données, mais ils seraient des références différentes.
Edit après les commentaires
Je veux juste être clair sur la distinction entre le moment où les choses sont en fait côté client et quand ils ont fait côté serveur. Si vous faites référence à un IQueryable<T>
comme IEnumerable<T>
, seule l'interrogation de fait , après c'est un IEnumerable<T>
sera fait côté client. Par exemple, dire que j'ai cette table et une LINQ-to-SQL contexte:
Customer
-----------
CustomerId
FirstName
LastName
J'ai d'abord construire une requête sur la base d' FirstName
. Cela crée un IQueryable<Customer>
:
var query = from c in db.Customers where c.FirstName.StartsWith("Ad") select c;
Maintenant je passe cette requête à une fonction qui prend un IEnumerable<Customer>
et fait de filtrage basé sur LastName
:
public void DoStuff(IEnumerable<Customer> customers)
{
foreach(var cust in from c in customers where c.LastName.StartsWith("Ro"))
{
Console.WriteLine(cust.CustomerId);
}
}
Nous avons fait une deuxième requête ici, mais c'est tout à fait IEnumerable<Customer>
. Ce qui se passe ici est que la première requête sera évaluée, l'exécution de cette SQL:
select CustomerId, FirstName, LastName from Customer where FirstName like 'Ad%'
Donc on va ramener tout le monde qui est - FirstName
commence par "Ad"
. Notez qu'il n'y a rien ici à propos de LastName
. C'est parce que c'est le filtrage du côté client.
Une fois qu'il ramène ces résultats, le programme va alors effectuer une itération sur les résultats et de les livrer uniquement les enregistrements dont LastName
commence par "Ro"
. L'inconvénient de cette est que nous avons rapporté de données, à savoir, toutes les lignes dont LastName
ne pas commencer avec "Ro"
--qui pourrait avoir été filtré sur le serveur.