C# 在 Linq 中使用 IQueryable
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1578778/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using IQueryable with Linq
提问by user190560
What is the use of IQueryable
in the context of LINQ?
什么是使用IQueryable
在LINQ的背景下?
Is it used for developing extension methods or any other purpose?
它是用于开发扩展方法还是其他任何目的?
采纳答案by Reed Copsey
Marc Gravell's answeris very complete, but I thought I'd add something about this from the user's point of view, as well...
Marc Gravell 的回答非常完整,但我想我也会从用户的角度添加一些关于此的内容......
The main difference, from a user's perspective, is that, when you use IQueryable<T>
(with a provider that supports things correctly), you can save a lot of resources.
从用户的角度来看,主要区别在于,当您使用IQueryable<T>
(使用正确支持事物的提供程序)时,您可以节省大量资源。
For example, if you're working against a remote database, with many ORM systems, you have the option of fetching data from a table in two ways, one which returns IEnumerable<T>
, and one which returns an IQueryable<T>
. Say, for example, you have a Products table, and you want to get all of the products whose cost is >$25.
例如,如果您正在使用许多 ORM 系统处理远程数据库,您可以选择以两种方式从表中获取数据,一种返回IEnumerable<T>
,另一种返回IQueryable<T>
. 例如,假设您有一个 Products 表,您想获取所有成本 > 25 美元的产品。
If you do:
如果你这样做:
IEnumerable<Product> products = myORM.GetProducts();
var productsOver25 = products.Where(p => p.Cost >= 25.00);
What happens here, is the database loads all of the products, and passes them across the wire to your program. Your program then filters the data. In essence, the database does a SELECT * FROM Products
, and returns EVERY product to you.
这里发生的是,数据库加载所有产品,并将它们通过线路传递给您的程序。然后您的程序过滤数据。本质上,数据库执行SELECT * FROM Products
, 并将每个产品返回给您。
With the right IQueryable<T>
provider, on the other hand, you can do:
IQueryable<T>
另一方面,使用正确的提供商,您可以:
IQueryable<Product> products = myORM.GetQueryableProducts();
var productsOver25 = products.Where(p => p.Cost >= 25.00);
The code looks the same, but the difference here is that the SQL executed will be SELECT * FROM Products WHERE Cost >= 25
.
代码看起来一样,但这里的区别在于执行的 SQL 将是SELECT * FROM Products WHERE Cost >= 25
.
From your POV as a developer, this looks the same. However, from a performance standpoint, you may only return 2 records across the network instead of 20,000....
从您作为开发人员的 POV 来看,这看起来是一样的。但是,从性能的角度来看,您只能通过网络返回 2 条记录,而不是 20,000 条......
回答by Marc Gravell
In essence its job is very similar to IEnumerable<T>
- to represent a queryable data source - the difference being that the various LINQ methods (on Queryable
) can be more specific, to build the query using Expression
trees rather than delegates (which is what Enumerable
uses).
本质上,它的工作非常类似于IEnumerable<T>
- 表示可查询的数据源 - 不同之处在于各种 LINQ 方法(on Queryable
)可以更具体,使用Expression
树而不是委托(这是Enumerable
使用的)构建查询。
The expression trees can be inspected by your chosen LINQ provider and turned into an actualquery - although that is a black art in itself.
表达式树可以由您选择的 LINQ 提供程序检查并转换为实际查询 - 尽管这本身就是一种黑色艺术。
This is really down to the ElementType
, Expression
and Provider
- but in reality you rarelyneed to care about this as a user. Only a LINQ implementerneeds to know the gory details.
这真的取决于ElementType
,Expression
而且Provider
- 但实际上,作为用户,您很少需要关心这一点。只有 LINQ实现者需要知道血腥的细节。
Re comments; I'm not quite sure what you want by way of example, but consider LINQ-to-SQL; the central object here is a DataContext
, which represents our database-wrapper. This typically has a property per table (for example, Customers
), and a table implements IQueryable<Customer>
. But we don't use that much directly; consider:
重新评论;例如,我不太确定您想要什么,但请考虑 LINQ-to-SQL;这里的中心对象是 a 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);
}
}
this becomes (by the C# compiler):
这变成(由 C# 编译器):
var qry = ctx.Customers.Where(cust => cust.Region == "North")
.Select(cust => new { cust.Id, cust.Name });
which is again interpreted (by the C# compiler) as:
再次解释(由 C# 编译器)为:
var qry = Queryable.Select(
Queryable.Where(
ctx.Customers,
cust => cust.Region == "North"),
cust => new { cust.Id, cust.Name });
Importantly, the static methods on Queryable
take expression trees, which - rather than regular IL, get compiled to an object model. For example - just looking at the "Where", this gives us something comparable to:
重要的是,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) ...
Didn't the compiler do a lot for us? This object model can be torn apart, inspected for what it means, and put back together again by the TSQL generator - giving something like:
编译器不是为我们做了很多吗?这个对象模型可以被拆开,检查它的含义,然后由 TSQL 生成器重新组合在一起 - 给出如下内容:
SELECT c.Id, c.Name
FROM [dbo].[Customer] c
WHERE c.Region = 'North'
(the string might end up as a parameter; I can't remember)
(字符串可能最终作为参数;我不记得了)
None of this would be possible if we had just used a delegate. And thisis the point of Queryable
/ IQueryable<T>
: it provides the entry-point for using expression trees.
如果我们只使用一个委托,这一切都不可能实现。而这是点Queryable
/ IQueryable<T>
:它提供了入口点使用表达式树。
All this is very complex, so it is a good job that the compiler makes it nice and easy for us.
所有这一切都非常复杂,所以编译器让我们做得很好而且很容易,这是一项很好的工作。
For more information, look at "C# in Depth" or "LINQ in Action", both of which provide coverage of these topics.
有关更多信息,请查看“ C# in Depth”或“ LINQ in Action”,两者都涵盖了这些主题。
回答by dove
It allows for further querying further down the line. If this was beyond a service boundary say, then the user of this IQueryable object would be allowed to do more with it.
它允许进一步查询。如果这超出了服务边界,那么这个 IQueryable 对象的用户将被允许用它做更多的事情。
For instance if you were using lazy loading with nhibernate this might result in graph being loaded when/if needed.
例如,如果您在 nhibernate 中使用延迟加载,这可能会导致在需要时/如果需要加载图形。
回答by Moumit
Although Reed Copseyand Marc Gravellalready described about IQueryable
(and also IEnumerable
) enough,mI want to add little more here by providing a small example on IQueryable
and IEnumerable
as many users asked for it
尽管Reed Copsey和Marc Gravell已经描述了IQueryable
(并且也IEnumerable
)了足够多的内容,但我想在这里通过提供一个小示例IQueryable
以及IEnumerable
许多用户要求的小示例来添加更多内容
Example: I have created two table in database
示例:我在数据库中创建了两个表
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)
The Primary key(PersonId
) of table Employee
is also a forgein key(personid
) of table Person
表的主键(PersonId
)也是表Employee
的外键(personid
)Person
Next i added ado.net entity model in my application and create below service class on that
接下来我在我的应用程序中添加了 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;
}
}
they contains same linq. It called in program.cs
as defined below
它们包含相同的 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();
}
}
The output is same for both obviously
显然两者的输出相同
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
So the question is what/where is the difference? It does not seem to have any difference right? Really!!
所以问题是什么/哪里有区别?好像没什么区别吧?真的!!
Let's have a look on sql queries generated and executed by entity framwork 5 during these period
来看看实体框架5在这期间生成并执行的sql查询
IQueryable execution part
IQueryable 执行部分
--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
IEnumerable 执行部分
--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 you have few questions now, let me guess those and try to answer them
所以你现在有几个问题,让我猜猜这些并尝试回答它们
Why are different scripts generated for same result?
为什么为相同的结果生成不同的脚本?
Lets find out some points here,
让我们在这里找出一些要点,
all queries has one common part
所有查询都有一个共同部分
WHERE [Extent1].[PersonId] IN (0,1,2,3)
WHERE [Extent1].[PersonId] IN (0,1,2,3)
why? Because both function IQueryable<Employee> GetEmployeeAndPersonDetailIQueryable
and
IEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerable
of SomeServiceClass
contains one common line in linq queries
为什么?因为 functionIQueryable<Employee> GetEmployeeAndPersonDetailIQueryable
和
IEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerable
ofSomeServiceClass
在 linq 查询中都包含一个公共行
where employeesToCollect.Contains(e.PersonId)
where employeesToCollect.Contains(e.PersonId)
Than why is the
AND (N'M' = [Extent1].[Gender])
part is missing in IEnumerable
execution part, while in both function calling we used Where(i => i.Gender == "M") in
program.cs`
为什么
AND (N'M' = [Extent1].[Gender])
在IEnumerable
执行部分缺少该部分,而在两个函数调用中我们都使用了Where(i => i.Gender == "M") in
program.cs`
Now we are in the point where difference came between
IQueryable
andIEnumerable
现在我们正处于
IQueryable
和 之间的差异点IEnumerable
What entity framwork does when an IQueryable
method called, it tooks linq statement written inside the method and try to find out if more linq expressions are defined on the resultset, it then gathers all linq queries defined until the result need to fetch and constructs more appropriate sql query to execute.
当一个IQueryable
方法被调用时,实体框架做了什么,它需要在方法内部编写 linq 语句,并尝试找出结果集上是否定义了更多的 linq 表达式,然后收集所有定义的 linq 查询,直到需要获取结果并构造更合适的 sql要执行的查询。
It provide a lots of benefits like,
它提供了很多好处,例如,
- only those rows populated by sql server which could be valid by the whole linq query execution
- helps sql server performance by not selecting unnecessary rows
- network cost get reduce
- 只有那些由 sql server 填充的行可能对整个 linq 查询执行有效
- 通过不选择不必要的行来帮助 sql server 性能
- 网络成本得到降低
like here in example sql server returned to application only two rows after IQueryable execution` but returned THREErows for IEnumerable query why?
喜欢这里例如在SQL Server的IQueryable execution`后只有两排返回到应用程序,但返回的三排为IEnumerable的查询,为什么?
In case of IEnumerable
method, entity framework took linq statement written inside the method and constructs sql query when result need to fetch. it does not include rest linq part to constructs the sql query. Like here no filtering is done in sql server on column gender
.
在IEnumerable
方法的情况下 ,实体框架采用方法内部编写的 linq 语句,并在需要获取结果时构造 sql 查询。它不包括用于构建 sql 查询的其余 linq 部分。像这里一样,在 sql server 中没有对 column 进行过滤gender
。
But the outputs are same? Because 'IEnumerable filters the result further in application level after retrieving result from sql server
但是输出是一样的吗?因为 'IEnumerable 在从 sql server 检索结果后在应用程序级别进一步过滤结果
SO,what should someone choose?
I personally prefer to define function result as IQueryable<T>
because there are lots of benefit it has over IEnumerable
like, you could join two or more IQueryable functions, which generate more specific script to sql server.
那么,人们应该选择什么?我个人更喜欢定义函数结果,IQueryable<T>
因为它有很多好处IEnumerable
,您可以加入两个或多个 IQueryable 函数,从而为 sql server 生成更具体的脚本。
Here in example you can see an IQueryable Query(IQueryableQuery2)
generates a more specific script than IEnumerable query(IEnumerableQuery2)
which is much more acceptable in my point of view.
在示例中,您可以看到IQueryable Query(IQueryableQuery2)
生成了一个更具体的脚本,IEnumerable query(IEnumerableQuery2)
而在我看来,这个脚本更容易接受。