2009-10-16 4 views

Antwort

421

Marc Gravell's answer ist sehr komplett, aber ich dachte, dass ich etwas über diesen aus Anwendersicht hinzufügen würde, wie gut ...


Der Hauptunterschied, aus der Sicht des Benutzers, ist, dass, Wenn Sie IQueryable<T> verwenden (mit einem Anbieter, der Dinge richtig unterstützt), können Sie eine Menge Ressourcen speichern. Wenn Sie z. B. mit einer entfernten Datenbank arbeiten und viele ORM-Systeme verwenden, haben Sie die Möglichkeit, Daten aus einer Tabelle auf zwei Arten abzurufen: Eine, die IEnumerable<T> zurückgibt, und eine, die eine IQueryable<T> zurückgibt. Angenommen, Sie haben eine Produkttabelle und möchten alle Produkte mit Kosten von> 25 € abrufen.

Wenn Sie das tun:

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

Was hier passiert, ist die Datenbank lädt alle der Produkte und übergibt sie an das Programm über den Draht. Ihr Programm filtert dann die Daten. Im Wesentlichen führt die Datenbank eine SELECT * FROM Products aus und gibt JEDES Produkt an Sie zurück.

Mit den richtigen IQueryable<T> Anbietern, auf der anderen Seite, können Sie tun:

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

Der Code sieht gleich aus, aber der Unterschied ist, dass der ausgeführten SQL SELECT * FROM Products WHERE Cost >= 25 sein wird.

Von Ihrem POV als Entwickler sieht das gleich aus. Doch aus anwendungstechnischer Sicht können Sie nur zurückgeben 2 Datensätze über das Netzwerk statt 20.000 ....

+7

Wo ist die Definition von "GetQueryableProducts();"? – Pankaj

+9

@StackOverflowUser Es soll eine beliebige Methode sein, die ein 'IQueryable ' zurückgibt - wäre spezifisch für Ihr ORM oder Repository usw. –

+0

richtig. aber Sie erwähnten Where-Klausel nach diesem Funktionsaufruf. Das System ist sich des Filters nicht bewusst. Ich meine es holen immer noch alle Rekorde von Produkten ab. Recht? – Pankaj

164

Im Wesentlichen seiner Arbeit zu IEnumerable<T> sehr ähnlich ist - eine abfragbaren Datenquelle darstellen - das ist Unterschied, dass die verschiedenen LINQ-Methoden (auf Queryable) spezifischer sein können, die Abfrage mit Expression Bäumen zu bauen, anstatt Delegierten (die ist was Enumerable verwendet).

Die Ausdrucksbäume können von Ihrem ausgewählten LINQ-Anbieter überprüft und in eine tatsächliche Abfrage umgewandelt werden - obwohl das eine schwarze Kunst an sich ist.

Das ist wirklich auf die ElementType, Expression und Provider - aber in Wirklichkeit Sie selten Notwendigkeit darüber, wie ein Benutzer zu kümmern. Nur ein LINQ Implementierer muss die blutigen Details kennen.


Re Kommentare; Ich bin nicht ganz sicher, was Sie als Beispiel wollen, aber betrachten Sie LINQ-to-SQL; Das zentrale Objekt ist hier eine DataContext, die unseren Datenbank-Wrapper darstellt. Dies hat normalerweise eine Eigenschaft pro Tabelle (z. B. Customers) und eine Tabelle implementiert IQueryable<Customer>. Aber wir verwenden nicht so viel direkt; berücksichtigen:

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); 
    } 
} 

Diese wird (durch die C# Compiler):

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

die wiederum (durch den C# Compiler) interpretiert wird als:

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

Wichtig ist, auf den statischen Methoden Queryable nehmen Ausdrucksbäume, die - anstatt regulärer IL, zu einem Objektmodell kompiliert werden. Zum Beispiel - nur an der „Wo?“ Sucht, gibt uns etwas vergleichbar:

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) ... 

hat noch keinen Compiler für uns viel zu tun? Dieses Objektmodell kann auseinandergerissen werden, kontrolliert, was es bedeutet, und durch den TSQL Generator wieder zusammen setzen - was so etwas wie:

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

(der String kann bis als Parameter am Ende, ich kann mich nicht erinnern,)

Nichts davon wäre möglich, wenn wir gerade einen Delegierten verwendet hätten. Und dieser ist der Punkt von Queryable/IQueryable<T>: es bietet den Einstiegspunkt für die Verwendung von Ausdrucksbäumen.

All dies ist sehr komplex, also ist es ein guter Job, dass der Compiler es für uns einfach und leicht macht. Weitere Informationen finden Sie unter "C# in Depth" oder "LINQ in Action", die beide diese Themen abdecken.

+2

Wenn es Ihnen nichts ausmacht können Sie mich mit einfach verständlichen Beispiel aktualisieren (wenn Sie Zeit haben). – user190560

+0

Können Sie erklären "Wo ist die Definition von" GetQueryableProducts(); "?" in Herr Reed Copsey antworten – Pankaj

+0

Genossen die Linie der Übersetzung von Ausdrücken zu einer Abfrage ist "schwarze Kunst an sich" ... eine Menge Wahrheit zu diesem – afreeland

2

Es ermöglicht weitere Abfragen weiter unten auf der Linie.Wenn dies jenseits einer Dienstgrenze war, würde dem Benutzer dieses IQueryable-Objekts erlaubt werden, mehr damit zu tun.

Zum Beispiel, wenn Sie Lazy Loading mit Nhibernate verwenden, kann dies dazu führen, dass das Diagramm bei Bedarf geladen wird.

10

Obwohl Reed Copsey und Marc Gravell bereits über IQueryable(and also IEnumerable) genug beschrieben, obwohl ich wenig mehr hier hinzufügen möchten, indem Sie eine small example on IQueryable and IEnumerable Bereitstellung als viele Nutzer gefragt davon

Beispiel: I zwei Tabelle in der Datenbank erstellt haben

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) der Tabelle Employee ist auch forgein key(personid) der Tabelle Person

Next i hinzugefügt ado.net Entitätsmodell in meiner Anwendung und erstellen unter Serviceklasse auf diesem

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; 
    } 
} 

sie gleiche Linq enthält.Er rief in program.cs wie unten

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(); 
    } 
} 

Die output is same for both offensichtlich definiert

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 

Die Frage ist also, was/wo ist der Unterschied? Es scheint nicht irgendeinen Unterschied richtig zu haben? Ja wirklich!!

Lassen Sie uns einen Blick auf SQL-Abfragen generiert und ausgeführt von Unternehmen framwork 5 während dieser Periode

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 
*/ 

So Sie jetzt einige Fragen haben, lassen Sie mich diejenigen erraten und versuchen, sie

Lets einige herauszufinden, zu beantworten Punkte hier,

alle Anfragen hat einen gemeinsamen Teil

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

warum? Da sowohl Funktion IQueryable<Employee> GetEmployeeAndPersonDetailIQueryable und IEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerable von SomeServiceClass ein enthält common line in linq queries

where employeesToCollect.Contains(e.PersonId)

als warum AND (N'M' = [Extent1].[Gender]) Teil in IEnumerable execution part fehlt, während in Funktion rufen wir Where(i => i.Gender == "M") in program.cs verwendet

Jetzt sind wir in dem Punkt, wo der Unterschied zwischen IQueryable und kam

Was entity framwork tut, wenn eine IQueryable Methode aufgerufen, es linq statement written inside the method tooks und versucht, wenn more linq/expression is defind on the resultset, um herauszufinden, es alle LINQ-Abfragen, bis das Ergebnis definiert sammeln müssen holen und constructs more appropriate sql query to execute.

Es bietet eine Menge Vorteile, wie,

  • nur die Zeilen von SQL Server bevölkerten, die von der ganzen Linq-Abfrage Ausführung gültig sein könnte
  • SQL Server-Leistung hilft nicht unnötig Reihen
  • Auswahl
  • Netzwerkkosten reduzieren erhalten

wie hier im Server Beispiel sQL zurückgegeben Anwendung nur two rows after IQueryable execution aber returned THREE rows for IEnumerable query warum?

Im Fall der IEnumerable Methode, entity framework nahm Linq-Anweisung in der Methode geschrieben und erstellt SQL-Abfrage, wenn Ergebnis abrufen müssen. it does not include rest linq part to constructs the sql query. Wie hier ist keine Filterung in SQL-Server auf Spalte gender getan.

Aber die Ausgänge sind gleich? Denn IEnumerable filtert das Ergebnis weiter in der Anwendungsebene nach dem Abrufen des Ergebnisses von sql server

SO, was sollte jemand wählen? Ich persönlich bevorzuge, Funktionsergebnis als IQueryable<T> zu definieren, weil es viele Vorteile gibt, die es über "IEnumerable" mag, konnten Sie join two or more IQueryable function, die spezifischeren Index zum sql Server erzeugen.

Hier im Beispiel können Sie sehen, IQueryable Query(IQueryableQuery2) mehr spezifische Skript als IEnumerable query(IEnumerableQuery2) generieren, die in my point of view viel akzeptabler ist.