2009-10-16 47 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中更加可接受。