Ree*_*sey 483
Marc Gravell的答案非常完整,但我想我会从用户的角度添加一些内容,以及......
从用户的角度来看,主要区别在于,当您使用IQueryable<T>(使用支持正确的提供程序)时,您可以节省大量资源.
例如,如果您正在使用许多ORM系统对远程数据库进行操作,则可以选择以两种方式从表中获取数据,一种返回IEnumerable<T>,另一种返回IQueryable<T>.比如说,您有一个Products表,并且您希望获得成本> 25美元的所有产品.
如果你这样做:
IEnumerable<Product> products = myORM.GetProducts();
var productsOver25 = products.Where(p => p.Cost >= 25.00);
Run Code Online (Sandbox Code Playgroud)
这里发生的是数据库加载所有产品,并将它们通过网络传递给您的程序.然后,您的程序会过滤数据.从本质上讲,数据库会执行a SELECT * FROM Products,并将每个产品返回给您.
IQueryable<T>另一方面,使用合适的提供商,您可以:
IQueryable<Product> products = myORM.GetQueryableProducts();
var productsOver25 = products.Where(p => p.Cost >= 25.00);
Run Code Online (Sandbox Code Playgroud)
代码看起来是一样的,但这里的区别在于执行的SQL将是SELECT * FROM Products WHERE Cost >= 25.
从您的POV作为开发人员,这看起来是一样的.但是,从性能角度来看,您只能在网络上返回2条记录,而不是20,000条....
Mar*_*ell 181
从本质上讲,它的工作非常类似于IEnumerable<T>- 表示可查询的数据源 - 区别在于各种LINQ方法(on Queryable)可以更具体,使用Expression树而不是委托构建查询(这是Enumerable使用的).
表达式树可以由您选择的LINQ提供程序进行检查,并转换为实际查询 - 尽管这本身就是一种黑色艺术.
这实际上是ElementType,Expression并且Provider- 但实际上你很少需要关心这个用户.只有LINQ 实现者需要了解血腥细节.
评论; 我不太确定你想要的例子,但考虑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);
}
}
Run Code Online (Sandbox Code Playgroud)
这变成了(通过C#编译器):
var qry = ctx.Customers.Where(cust => cust.Region == "North")
.Select(cust => new { cust.Id, cust.Name });
Run Code Online (Sandbox Code Playgroud)
再次解释(由C#编译器):
var qry = Queryable.Select(
Queryable.Where(
ctx.Customers,
cust => cust.Region == "North"),
cust => new { cust.Id, cust.Name });
Run Code Online (Sandbox Code Playgroud)
重要的是,Queryable采用表达式树的静态方法- 而不是常规的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) ...
Run Code Online (Sandbox Code Playgroud)
编译器没有为我们做很多事吗?这个对象模型可以拆开,检查它的含义,然后通过TSQL生成器重新组合在一起 - 给出类似的东西:
SELECT c.Id, c.Name
FROM [dbo].[Customer] c
WHERE c.Region = 'North'
Run Code Online (Sandbox Code Playgroud)
(字符串可能最终作为参数;我不记得了)
如果我们刚刚使用了一个代表,这一切都不可能.而这是点Queryable/ IQueryable<T>:它提供了入口点使用表达式树.
所有这一切都非常复杂,所以编译器使它对我们来说很容易也很好.
有关更多信息,请查看" C#in Depth "或" LINQ in Action ",它们都提供了这些主题的内容.
Mou*_*mit 14
虽然Reed Copsey和Marc Gravell已经描述了IQueryable(也有IEnumerable)足够的内容,但我希望通过提供一个小例子IQueryable以及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)
Run Code Online (Sandbox Code Playgroud)
表的主键(PersonId)也是表Employee的forgein键(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;
}
}
Run Code Online (Sandbox Code Playgroud)
它们包含相同的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();
}
}
Run Code Online (Sandbox Code Playgroud)
显然,两者的输出相同
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
Run Code Online (Sandbox Code Playgroud)
所以问题是差异在哪里/哪里?它似乎没有任何区别对吗?真!!
让我们来看看在这段时间内由实体框架5生成和执行的sql查询
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]
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
两个执行部分的通用脚本
/* 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
*/
Run Code Online (Sandbox Code Playgroud)
所以你现在几乎没有问题,让我猜那些并试着回答它们
为什么为同一结果生成不同的脚本?
让我们在这里找到一些观点,
所有查询都有一个共同点
WHERE [Extent1].[PersonId] IN (0,1,2,3)
为什么?因为这两个函数IQueryable<Employee> GetEmployeeAndPersonDetailIQueryable和
IEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerable的SomeServiceClass包含LINQ查询一条共用线
where employeesToCollect.Contains(e.PersonId)
为什么
AND (N'M' = [Extent1].[Gender])部分在IEnumerable执行部分缺失,而在两个函数调用中我们都使用Where(i => i.Gender == "M") inprogram.cs`
现在我们在点差之间来了
IQueryable和IEnumerable
当一个IQueryable方法调用时,它执行了什么实体框架,它在方法内部编写了linq语句,并试图找出是否在结果集上定义了更多的linq表达式,然后收集所有定义的linq查询,直到结果需要获取并构造更合适的sql查询执行.
它提供了很多好处,比如
像在这里例如SQL服务器返回给应用程序的IQueryable execution`后只有两行,但返回的三排为IEnumerable的查询,为什么?
在IEnumerable方法的情况下 ,实体框架在方法内部编写了linq语句,并在需要获取结果时构造sql查询.它不包括rest linq部分来构造sql查询.像这里没有在列上的sql server中进行过滤gender.
但输出是一样的吗?因为'IEnumerable在从sql server检索结果后在应用程序级别进一步过滤结果
SO,我应该有人选择呢?我个人比较喜欢自定义函数的结果IQueryable<T>,因为有很多的好处它比IEnumerable一样,你可以连接两个或更多的IQueryable功能,产生更具体的脚本到SQL Server.
在这个示例中,您可以看到IQueryable Query(IQueryableQuery2)生成一个更具体的脚本IEnumerable query(IEnumerableQuery2),在我看来,这个脚本更容易被接受.