与Linq一起使用IQueryable

在LINQ的上下文中使用IQueryable什么用?

用于开发扩展方法还是用于其他目的?

Marc Gravell的回答非常完整,但是我认为我会从用户的angular度join一些关于此的内容,以及…


从用户的angular度来看,主要区别在于,当您使用IQueryable<T> (提供者支持正确的东西)时,可以节省大量资源。

例如,如果您正在使用远程数据库(使用多个ORM系统),则可以select从两种方式获取表中的数据,一种返回IEnumerable<T> ,另一种返回IQueryable<T> 。 比如说,你有一个产品表,你想要得到所有成本大于25美元的产品。

如果你这样做:

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

这里发生的是,数据库加载所有的产品,并通过networking传递给你的程序。 您的程序然后过滤数据。 本质上,数据库执行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开发者来看,这看起来是一样的。 但是,从性能的angular度来看,您可能只能在networking上返回2条logging,而不是2万条….

实质上,它的工作与IEnumerable<T>非常相似 – 代表一个可查询的数据源 – 区别在于各种LINQ方法(在Queryable )可以更具体,使用Expression树而不是委托来构build查询Enumerable使用什么)。

expression式树可以被你select的LINQ提供者检查,并转化为一个实际的查询 – 尽pipe这本身就是一种黑色艺术。

这实际上ElementTypeExpressionProvider – 但实际上你很less需要关心这个作为用户 。 只有一个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上的静态方法需要expression式树,而不是普通的IL被编译成对象模型。 例如 – 只要看一下“Where”,这就给了我们一些东西:

 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' 

(该string可能会以参数结束;我不记得了)

如果我们刚刚使用委托,这些都不可能。 Queryable / IQueryable<T> :它提供了使用expression式树的入口点。

所有这些都非常复杂,所以编译器对我们来说很好,很容易。

有关更多信息,请参阅“ C#深度 ”或“ LINQ in Action ”,这两个主题都涵盖了这些主题。

尽pipeReed Copsey和Marc Gravell已经描述了IQueryable(and also IEnumerable) ,但是我想通过small example on IQueryable and IEnumerable提供一个small example on IQueryable and IEnumerable来添加更多内容small example on IQueryable and IEnumerable因为很多用户都会问这个问题

例如 :我在数据库中创build了两个表

  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) 

Employee Primary key(PersonId)也是表Person forgein key(personid)

接下来,我在我的应用程序中添加了ado.net实体模型,并在其上创build了以下服务类

 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 bothoutput 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 

所以问题是什么/哪里有区别? 它似乎没有任何区别? 真!!

让我们来看看在这段时间内由entity framework5生成和执行的sql查询

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

所以你现在有几个问题,让我猜这些,并试图回答他们

Why different script is generated for same result?

让我们来看看这里的一些观点,

所有查询都有一个共同的部分

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

为什么? 因为函数IQueryable<Employee> GetEmployeeAndPersonDetailIQueryableIEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerable SomeServiceClass common line in linq queries包含一条common line in linq queries

where employeesToCollect.Contains(e.PersonId)

比在IEnumerable execution part为什么AND (N'M' = [Extent1].[Gender])部分缺失,而在这两个函数调用中,我们在program.cs使用Where(i => i.Gender == "M")

现在我们处于IQueryableIEnumerable之间的区别了

当一个IQueryable方法调用时,什么entity framwork会执行,它将LINQ linq statement written inside the method并试图找出more linq/expression is defind on the resultset定义在结果more linq/expression is defind on the resultset ,它收集定义的所有LINQ查询,直到结果需要获取并constructs more appropriate sql query to execute

它提供了很多好处,

  • 只有那些由sql server填充的行可以被整个linq查询执行有效
  • 通过不select不必要的行来帮助sql server性能
  • networking成本得到降低

像这里在示例sql服务器返回到应用程序two rows after IQueryable execution只有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服务器上的列gender

但是输出是一样的? 因为“IEnumerable从sql server检索结果后在应用程序级别进一步筛选结果

那么,应该select什么? 我个人更喜欢将函数结果定义为IQueryable<T>因为它有很多好处,比如'IEnumerable',你可以join two or more IQueryable function ,这些join two or more IQueryable function生成更具体的脚本到sql server。

在这个例子中,你可以看到一个IQueryable Query(IQueryableQuery2)生成比IEnumerable query(IEnumerableQuery2)更具体的脚本,这在my point of view更加可以接受。

它允许进一步查询下行。 如果这超出了服务边界的话,那么这个IQueryable对象的用户将被允许对它做更多的事情。

例如,如果您使用惰性加载与nhibernate这可能会导致图加载时/如果需要的话。