2009-10-16 7 views

Répondre

421

Marc Gravell's answer est très complet, mais je pensais que j'ajouter quelque chose à ce sujet du point de vue de l'utilisateur, ainsi ...


La principale différence, du point de vue d'un utilisateur, est que, Lorsque vous utilisez IQueryable<T> (avec un fournisseur qui prend en charge les choses correctement), vous pouvez économiser beaucoup de ressources. Par exemple, si vous travaillez avec une base de données distante, avec de nombreux systèmes ORM, vous avez la possibilité d'extraire les données d'une table de deux manières, une qui renvoie IEnumerable<T> et une qui renvoie IQueryable<T>. Supposons, par exemple, que vous ayez une table Produits et que vous souhaitiez obtenir tous les produits dont le coût est> 25 $.

Si vous faites:

IEnumerable<Product> products = myORM.GetProducts(); 
var productsOver25 = products.Where(p => p.Cost >= 25.00); 

Qu'est-ce qui se passe ici, est la charge de base de données tous les produits, et les transmet à travers le fil à votre programme. Votre programme filtre ensuite les données. En substance, la base de données effectue un SELECT * FROM Products et vous renvoie TOUS les produits.

Avec le fournisseur IQueryable<T> droit, d'autre part, vous pouvez le faire:

IQueryable<Product> products = myORM.GetQueryableProducts(); 
var productsOver25 = products.Where(p => p.Cost >= 25.00); 

Le code semble le même, mais la différence est que le SQL exécuté sera SELECT * FROM Products WHERE Cost >= 25. De votre point de vue en tant que développeur, le résultat est le même. Cependant, du point de vue de la performance, vous ne pouvez revenir 2 dossiers sur le réseau au lieu de 20 000 ....

+7

Où est la définition de "GetQueryableProducts();"? – Pankaj

+9

@StackOverflowUser Il est destiné à être toute méthode qui retourne un 'IQueryable ' - serait spécifique à votre ORM ou référentiel, etc –

+0

droite. mais vous avez mentionné où la clause après cet appel de fonction. Le système n'est donc toujours pas conscient du filtre. Je veux dire qu'il va chercher tous les records de produits. droite? – Pankaj

164

Essentiellement son travail est très similaire à IEnumerable<T> - pour représenter une source de données interrogeable - la différence étant que les différentes méthodes de LINQ (sur Queryable) peuvent être plus spécifiques, pour construire la requête en utilisant Expression arbres plutôt que des délégués (qui est ce que Enumerable utilise).

Les arborescences d'expression peuvent être inspectées par votre fournisseur LINQ choisi et transformées en une requête réelle - bien que ce soit un art noir en soi.

Ceci est vraiment à la ElementType, Expression et Provider - mais en réalité vous rarement besoin de se soucier de cela comme un utilisateur. Seul un implémenteur LINQ a besoin de connaître les détails sanglants.


Re commentaires; Je ne suis pas sûr de ce que vous voulez à titre d'exemple, mais considérons LINQ-to-SQL; l'objet central ici est un DataContext, qui représente notre gestionnaire de base de données. Cela a généralement une propriété par table (par exemple, Customers) et une table implémente IQueryable<Customer>. Mais nous n'utilisons pas beaucoup de choses directement. considérer:

using(var ctx = new MyDataContext()) { 
    var qry = from cust in ctx.Customers 
       where cust.Region == "North" 
       select new { cust.Id, cust.Name }; 
    foreach(var row in qry) { 
     Console.WriteLine("{0}: {1}", row.Id, row.Name); 
    } 
} 

cela devient (par le compilateur C#):

var qry = ctx.Customers.Where(cust => cust.Region == "North") 
       .Select(cust => new { cust.Id, cust.Name }); 

qui est interprété à nouveau (par le compilateur C#) comme:

var qry = Queryable.Select(
       Queryable.Where(
        ctx.Customers, 
        cust => cust.Region == "North"), 
       cust => new { cust.Id, cust.Name }); 

Fait important, les méthodes statiques sur Queryable prend les arbres d'expression, qui - plutôt que IL régulière, sont compilés à un modèle d'objet. Par exemple - simplement regarder le « où », cela nous donne quelque chose de comparable à:

var cust = Expression.Parameter(typeof(Customer), "cust"); 
var lambda = Expression.Lambda<Func<Customer,bool>>(
        Expression.Equal(
         Expression.Property(cust, "Region"), 
         Expression.Constant("North") 
       ), cust); 

... Queryable.Where(ctx.Customers, lambda) ... 

pas le compilateur Avez-faire beaucoup pour nous? Ce modèle d'objet peut être déchiré, inspecté pour ce que cela signifie, et remettre à nouveau ensemble par le générateur de TSQL - donner quelque chose comme:

SELECT c.Id, c.Name 
FROM [dbo].[Customer] c 
WHERE c.Region = 'North' 

(la chaîne pourrait finir comme paramètre, je ne me souviens pas

Rien de tout cela ne serait possible si nous venions d'utiliser un délégué. Et ce est le point de Queryable/IQueryable<T>: il fournit le point d'entrée pour l'utilisation des arbres d'expression.

Tout cela est très complexe, donc c'est un bon travail que le compilateur le rende agréable et facile pour nous.

Pour plus d'informations, consultez «C# in Depth» ou «LINQ in Action», qui couvrent tous les deux ces sujets.

+2

Si cela ne vous dérange pas pouvez-vous me mettre à jour avec un exemple simple et compréhensible (si vous avez le temps). – user190560

+0

Pouvez-vous expliquer "Où est la définition de" GetQueryableProducts(); "?" dans M. Reed Copsey réponse – Pankaj

+0

Apprécié la ligne d'expressions de traduction à une requête est "art noir en soi" ... beaucoup de vérité à cela – afreeland

2

Permet d'effectuer des requêtes supplémentaires plus loin dans la ligne.Si cela était au-delà d'une limite de service, alors l'utilisateur de cet objet IQueryable serait autorisé à en faire plus avec lui.

Par exemple, si vous utilisiez un chargement paresseux avec nhibernate, cela pourrait entraîner le chargement du graphique quand/si nécessaire.

10

Bien que Reed Copsey et Marc Gravell déjà décrit à propos assez IQueryable(and also IEnumerable), si je veux ajouter un peu plus ici en fournissant un small example on IQueryable and IEnumerable comme de nombreux utilisateurs qui lui sont posées

Exemple: J'ai créé deux tables dans la base

CREATE TABLE [dbo].[Employee]([PersonId] [int] NOT NULL PRIMARY KEY,[Gender] [nchar](1) NOT NULL) 
    CREATE TABLE [dbo].[Person]([PersonId] [int] NOT NULL PRIMARY KEY,[FirstName] [nvarchar](50) NOT NULL,[LastName] [nvarchar](50) NOT NULL) 

Primary key(PersonId) de la table Employee est également forgein key(personid) de la table Person

Next i ajouté un modèle d'entité ado.net dans mon application et créer ci-dessous classe de service sur cette

public class SomeServiceClass 
{ 
    public IQueryable<Employee> GetEmployeeAndPersonDetailIQueryable(IEnumerable<int> employeesToCollect) 
    { 
     DemoIQueryableEntities db = new DemoIQueryableEntities(); 
     var allDetails = from Employee e in db.Employees 
         join Person p in db.People on e.PersonId equals p.PersonId 
         where employeesToCollect.Contains(e.PersonId) 
         select e; 
     return allDetails; 
    } 

    public IEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerable(IEnumerable<int> employeesToCollect) 
    { 
     DemoIQueryableEntities db = new DemoIQueryableEntities(); 
     var allDetails = from Employee e in db.Employees 
         join Person p in db.People on e.PersonId equals p.PersonId 
         where employeesToCollect.Contains(e.PersonId) 
         select e; 
     return allDetails; 
    } 
} 

ils contiennent même LINQ.Il a appelé à program.cs tel que défini ci-dessous

class Program 
{ 
    static void Main(string[] args) 
    { 
     SomeServiceClass s= new SomeServiceClass(); 

     var employeesToCollect= new []{0,1,2,3}; 

     //IQueryable execution part 
     var IQueryableList = s.GetEmployeeAndPersonDetailIQueryable(employeesToCollect).Where(i => i.Gender=="M");    
     foreach (var emp in IQueryableList) 
     { 
      System.Console.WriteLine("ID:{0}, EName:{1},Gender:{2}", emp.PersonId, emp.Person.FirstName, emp.Gender); 
     } 
     System.Console.WriteLine("IQueryable contain {0} row in result set", IQueryableList.Count()); 

     //IEnumerable execution part 
     var IEnumerableList = s.GetEmployeeAndPersonDetailIEnumerable(employeesToCollect).Where(i => i.Gender == "M"); 
     foreach (var emp in IEnumerableList) 
     { 
      System.Console.WriteLine("ID:{0}, EName:{1},Gender:{2}", emp.PersonId, emp.Person.FirstName, emp.Gender); 
     } 
     System.Console.WriteLine("IEnumerable contain {0} row in result set", IEnumerableList.Count()); 

     Console.ReadKey(); 
    } 
} 

Le output is same for both évidemment

ID:1, EName:Ken,Gender:M 
ID:3, EName:Roberto,Gender:M 
IQueryable contain 2 row in result set 
ID:1, EName:Ken,Gender:M 
ID:3, EName:Roberto,Gender:M 
IEnumerable contain 2 row in result set 

La question est ce que/où est la différence? Il ne semble pas avoir une différence non? Vraiment!!

Jetons un coup d'oeil sur les requêtes SQL générées et exécutées par une entité framwork 5 au cours de ces périodes

IQueryable execution part

--IQueryableQuery1 
SELECT 
[Extent1].[PersonId] AS [PersonId], 
[Extent1].[Gender] AS [Gender] 
FROM [dbo].[Employee] AS [Extent1] 
WHERE ([Extent1].[PersonId] IN (0,1,2,3)) AND (N'M' = [Extent1].[Gender]) 

--IQueryableQuery2 
SELECT 
[GroupBy1].[A1] AS [C1] 
FROM (SELECT 
    COUNT(1) AS [A1] 
    FROM [dbo].[Employee] AS [Extent1] 
    WHERE ([Extent1].[PersonId] IN (0,1,2,3)) AND (N'M' = [Extent1].[Gender]) 
) AS [GroupBy1] 

IEnumerable execution part

--IEnumerableQuery1 
SELECT 
[Extent1].[PersonId] AS [PersonId], 
[Extent1].[Gender] AS [Gender] 
FROM [dbo].[Employee] AS [Extent1] 
WHERE [Extent1].[PersonId] IN (0,1,2,3) 

--IEnumerableQuery2 
SELECT 
[Extent1].[PersonId] AS [PersonId], 
[Extent1].[Gender] AS [Gender] 
FROM [dbo].[Employee] AS [Extent1] 
WHERE [Extent1].[PersonId] IN (0,1,2,3) 

Common script for both execution part

/* these two query will execute for both IQueryable or IEnumerable to get details from Person table 
    Ignore these two queries here because it has nothing to do with IQueryable vs IEnumerable 
--ICommonQuery1 
exec sp_executesql N'SELECT 
[Extent1].[PersonId] AS [PersonId], 
[Extent1].[FirstName] AS [FirstName], 
[Extent1].[LastName] AS [LastName] 
FROM [dbo].[Person] AS [Extent1] 
WHERE [Extent1].[PersonId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1 

--ICommonQuery2 
exec sp_executesql N'SELECT 
[Extent1].[PersonId] AS [PersonId], 
[Extent1].[FirstName] AS [FirstName], 
[Extent1].[LastName] AS [LastName] 
FROM [dbo].[Person] AS [Extent1] 
WHERE [Extent1].[PersonId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=3 
*/ 

Vous avez quelques questions maintenant, laissez-moi deviner ceux-ci et essayer d'y répondre

permet de trouver quelques-uns points ici,

toutes les requêtes a une partie commune

WHERE [Extent1].[PersonId] IN (0,1,2,3)

pourquoi? Parce que les deux fonctions IQueryable<Employee> GetEmployeeAndPersonDetailIQueryable et IEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerable de SomeServiceClass contient une common line in linq queries

where employeesToCollect.Contains(e.PersonId)

que pourquoi AND (N'M' = [Extent1].[Gender]) partie est manquante dans IEnumerable execution part, alors que dans les deux fonctions d'appel que nous avons utilisé Where(i => i.Gender == "M") dans program.cs

Maintenant, nous sommes au point où la différence est venue entre IQueryable et IEnumerable

Qu'est-ce entity framwork fait quand une méthode IQueryable appelé, il tooks linq statement written inside the method et essayer de savoir si more linq/expression is defind on the resultset, il rassemble toutes les requêtes LINQ définies jusqu'à ce que le résultat doit chercher et constructs more appropriate sql query to execute.

Il offre un bon nombre d'avantages tels que,

  • que les lignes peuplées par le serveur SQL qui pourrait être valide par l'exécution de la requête toute LINQ
  • aide les performances du serveur SQL en ne sélectionnant pas les lignes inutiles
  • coût du réseau se réduire

comme ici dans l'exemple serveur SQL retourné à l'application seulement two rows after IQueryable execution mais returned THREE rows for IEnumerable query pourquoi?

Dans le cas de la méthode IEnumerable, entity framework a pris l'instruction linq écrite à l'intérieur de la méthode et construit la requête SQL lorsque le résultat doit être récupéré. it does not include rest linq part to constructs the sql query. Comme ici, aucun filtrage n'est effectué dans le serveur SQL sur la colonne gender.

Mais les sorties sont-elles identiques? Parce que 'IEnumerable filtre le résultat plus loin dans le niveau de l'application après la récupération résultat de sql server

SO, que devrait choisir quelqu'un? Personnellement, je préfère définir le résultat de la fonction comme IQueryable<T> car il y a beaucoup d'avantages sur «IEnumerable» comme, vous pouvez join two or more IQueryable function qui génèrent un script plus spécifique au serveur sql.

Ici, dans l'exemple, vous pouvez voir un IQueryable Query(IQueryableQuery2) générer un script plus spécifique que IEnumerable query(IEnumerableQuery2), ce qui est beaucoup plus acceptable dans my point of view.

Questions connexes