2009-10-16 76 views

回答

421

Marc Gravell's answer是非常完整的,但我想我會但從用戶的角度補充一些有關這一點,以及...


的主要區別,從用戶的角度來看,是,當您使用IQueryable<T>(使用支持正確內容的提供程序)時,可以節省大量資源。例如,如果您使用的是遠程數據庫(使用多個ORM系統),則可以通過兩種方式從表中獲取數據,其中一種返回IEnumerable<T>,另一種返回IQueryable<T>。比如說,你有一個產品表,並且你想要得到所有成本大於25美元的產品。

如果你這樣做:

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

這裏會發生什麼情況,是數據庫加載所有的產品,並將它們通過線路到您的程序。您的程序然後過濾數據。本質上,數據庫執行SELECT * FROM Products,並將每個產品都返回給您。

有了正確的IQueryable<T>提供商,在另一方面,你可以這樣做:

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

的代碼看起來是一樣的,但這裏的不同之處在於執行的SQL會SELECT * FROM Products WHERE Cost >= 25

從您的POV作爲開發人員,這看起來是一樣的。然而,從性能的角度來看,你可能只在網絡上,而不是20000返回2條記錄....

+7

「GetQueryableProducts();」的定義在哪裏? – Pankaj 2011-12-13 18:28:03

+9

@StackOverflowUser它的目的是任何方法,返回'IQueryable ' - 將特定於您的ORM或存儲庫等 – 2011-12-13 18:58:32

+0

權利。但是你在這個函數調用之後提到了where子句。所以系統仍然不知道過濾器。我的意思是它仍然可以獲取產品的所有記錄。對? – Pankaj 2011-12-14 04:49:59

164

在本質上它的工作是非常相似的IEnumerable<T> - 代表可查詢的數據源 - 的不同之處在於各種LINQ方法(上Queryable)可以更具體,建立使用Expression樹木,而不是代表查詢(其是什麼Enumerable使用)。

表達式樹可以由您選擇的LINQ提供程序檢查,並且變成實際的查詢 - 儘管這本身就是一種黑色藝術。

這是真的下到ElementTypeExpressionProvider - 但在現實中,你很少需要關心這個作爲用戶。只有LINQ 的實施者需要知道血淋淋的細節。


重新評論;我不太清楚你想要什麼,但考慮LINQ到SQL;這裏的中心對象是DataContext,它代表我們的數據庫包裝器。這通常每個表具有一個屬性(例如,Customers),並且一個表執行IQueryable<Customer>。但我們並沒有直接使用那麼多,考慮:

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

這成爲(由C#編譯器):

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

其再次解釋(由C#編譯器)爲:

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

重要的是,靜態方法上Queryable採取表達式樹,而不是常規的IL,將其編譯爲對象模型。例如 - 只是在「去哪兒」,這爲我們提供了一些類似:

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

沒有編譯器爲我們做了很多?此對象模型可以分開被撕裂,檢查是否意味着什麼,以及由TSQL發電機重新組裝 - 讓這樣的:

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

(字符串可能最終作爲參數,我不記得)

如果我們剛剛使用委託,這些都不可能。並且這個是點的Queryable/IQueryable<T>:它提供了使用表達式樹的入口點。

所有這些都非常複雜,所以編譯器對我們來說很好,很容易。

欲瞭解更多信息,請看「C# in Depth」或「LINQ in Action」,兩者都提供這些主題的報道。

+2

如果你不介意你可以用簡單易懂的例子更新我(如果你有時間的話)。 – user190560 2009-10-16 15:44:11

+0

您能否解釋「GetQueryableProducts()的定義在哪裏? 「?」在Reed Copsey先生的回覆中 – Pankaj 2011-12-13 18:31:38

+0

享受到查詢翻譯表達式的路線是「黑色藝術本身」......很多事實對於那 – afreeland 2013-12-16 18:27:06

2

它允許進一步查詢下行。如果這超出了服務邊界的話,那麼這個IQueryable對象的用戶將被允許對它做更多的事情。

例如,如果您使用惰性加載與nhibernate這可能會導致圖形加載/如果需要。

10

雖然​​和已經描述過的有關IQueryable(and also IEnumerable)足夠Marc Gravell,但我想在這裏通過提供small example on IQueryable and IEnumerable如添加更多一點許多用戶要求它

:我已經在數據庫中創建了兩個表

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) 

EmployeePrimary key(PersonId)也表Person

下一頁我加入ado.net實體模型在我的申請,並於該

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

它們含有相同的LINQ創建以下 服務類。它呼籲在program.cs下面

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

output is same for both明顯

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 

所以,問題是什麼/哪裏是區別定義?它好像沒有 有什麼區別吧?真!!

讓我們在這期間

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) 
對實體 framwork 5生成並執行SQL查詢來看看

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

所以,你現在有幾個問題,讓我猜那些並嘗試回答他們

讓我們找到了一些點這裏,

所有查詢都有一個共同的部分

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

爲什麼呢?因爲這兩個功能IQueryable<Employee> GetEmployeeAndPersonDetailIQueryableSomeServiceClass IEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerable包含一個common line in linq queries

where employeesToCollect.Contains(e.PersonId)

比爲什麼 AND (N'M' = [Extent1].[Gender])部分在IEnumerable execution part缺失,而在這兩個函數調用我們在program.cs

使用Where(i => i.Gender == "M")現在我們在IQueryable和 之間的差異

什麼entity framwork確實當IQueryable方法調用,它tooks linq statement written inside the method並試圖找出是否more linq/expression is defind on the resultset,它收集到的結果中定義的所有的LINQ查詢需要獲取和constructs more appropriate sql query to execute

它提供了許多像好處,

  • 僅由SQL Server填充這些行可能是由 整個LINQ查詢執行有效
  • 有助於通過不選擇不必要的行
  • SQL Server的性能
  • 網絡成本得到降低

像這裏在示例sql服務器只返回到應用程序two rows after IQueryable executionreturned THREE rows for IEnumerable query爲什麼?

IEnumerable方法的情況下,entity framework將linq語句寫入方法內部,並在結果需要獲取時構造sql查詢。 it does not include rest linq part to constructs the sql query。像這裏一樣,沒有在sql server中對列gender進行過濾。

但輸出是一樣的嗎?因爲'IEnumerable在檢索結果後在應用程序級別進一步篩選結果從sql server

SO,應該有人選擇什麼? 我個人更喜歡將函數結果定義爲IQueryable<T>,因爲它具有比'IEnumerable'更好的效果,您可以使用join two or more IQueryable function這些函數生成更具體的腳本到sql server。

這裏的示例中,您可以看到一個IQueryable Query(IQueryableQuery2)生成比IEnumerable query(IEnumerableQuery2)更具體的腳本,這在my point of view中更加可接受。