返回IQueryable<T>和IEnumerable<T>之间的区别是什么,什么时候应该优先于另一个?
IQueryable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;
IEnumerable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;
两者都将被延迟执行,何时应该优先于其中一个?
是的,两者都使用延迟执行。让我们使用SQL Server分析器....来说明两者的区别
当我们运行以下代码时:
MarketDevEntities db = new MarketDevEntities();
IEnumerable<WebLog> first = db.WebLogs;
var second = first.Where(c => c.DurationSeconds > 10);
var third = second.Where(c => c.WebLogID > 100);
var result = third.Where(c => c.EmailAddress.Length > 11);
Console.Write(result.First().UserName);
在SQL Server分析器中,我们发现一个命令等于:
"SELECT * FROM [dbo].[WebLog]"
在有100万条记录的WebLog表上运行这段代码大约需要90秒。
因此,所有的表记录都作为对象加载到内存中,然后每个. where()都将是内存中针对这些对象的另一个过滤器。
当我们在上面的例子(第二行)中使用IQueryable而不是IEnumerable时:
在SQL Server分析器中,我们发现一个命令等于:
"SELECT TOP 1 * FROM [dbo].[WebLog] WHERE [DurationSeconds] > 10 AND [WebLogID] > 100 AND LEN([EmailAddress]) > 11"
使用IQueryable运行这段代码大约需要4秒钟。
IQueryable有一个名为Expression的属性,它存储了一个树表达式,当我们在示例中使用结果时开始创建这个树表达式(称为延迟执行),在结束时,这个表达式将被转换为一个SQL查询,在数据库引擎上运行。
是的,两者都使用延迟执行。让我们使用SQL Server分析器....来说明两者的区别
当我们运行以下代码时:
MarketDevEntities db = new MarketDevEntities();
IEnumerable<WebLog> first = db.WebLogs;
var second = first.Where(c => c.DurationSeconds > 10);
var third = second.Where(c => c.WebLogID > 100);
var result = third.Where(c => c.EmailAddress.Length > 11);
Console.Write(result.First().UserName);
在SQL Server分析器中,我们发现一个命令等于:
"SELECT * FROM [dbo].[WebLog]"
在有100万条记录的WebLog表上运行这段代码大约需要90秒。
因此,所有的表记录都作为对象加载到内存中,然后每个. where()都将是内存中针对这些对象的另一个过滤器。
当我们在上面的例子(第二行)中使用IQueryable而不是IEnumerable时:
在SQL Server分析器中,我们发现一个命令等于:
"SELECT TOP 1 * FROM [dbo].[WebLog] WHERE [DurationSeconds] > 10 AND [WebLogID] > 100 AND LEN([EmailAddress]) > 11"
使用IQueryable运行这段代码大约需要4秒钟。
IQueryable有一个名为Expression的属性,它存储了一个树表达式,当我们在示例中使用结果时开始创建这个树表达式(称为延迟执行),在结束时,这个表达式将被转换为一个SQL查询,在数据库引擎上运行。
上面的答案很好,但它没有提到解释两个接口“如何”不同的表达式树。基本上,有两组相同的LINQ扩展。Where(), Sum(), Count(), FirstOrDefault()等都有两个版本:一个接受函数,一个接受表达式。
IEnumerable版本签名为:Where(Func<Customer, bool> predicate)
IQueryable版本签名是:Where(表达式<Func<Customer, bool>>谓词)
你可能在没有意识到的情况下使用了这两个函数,因为它们使用相同的语法调用:
例如,Where(x => x.City == "<City>")对IEnumerable和IQueryable都有效
当在IEnumerable集合上使用Where()时,编译器将一个编译后的函数传递给Where()
当在IQueryable集合上使用Where()时,编译器将表达式树传递给Where()。表达式树类似于反射系统,但用于代码。编译器将代码转换为数据结构,该结构以易于理解的格式描述代码的功能。
为什么要用这个表达式树呢?我只想让Where()过滤我的数据。
主要原因是EF和Linq2SQL orm都可以将表达式树直接转换为SQL,这样您的代码将执行得更快。
哦,这听起来像一个免费的性能提升,我应该在这种情况下使用AsQueryable()吗?
不,IQueryable只有在底层数据提供程序可以使用它时才有用。将常规列表转换为IQueryable不会给你带来任何好处。
是的,两者都会让你延期执行。
区别在于IQueryable<T>是允许LINQ-to- sql (LINQ. sql)的接口。对任何事情)去工作。因此,如果在IQueryable<T>上进一步细化查询,该查询将在数据库中执行(如果可能的话)。
对于IEnumerable<T>情况,它将是LINQ-to-object,这意味着所有与原始查询匹配的对象都必须从数据库加载到内存中。
在代码:
IQueryable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);
该代码将只对选择的黄金客户执行SQL。另一方面,下面的代码将在数据库中执行原始查询,然后在内存中过滤出非黄金客户:
IEnumerable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);
这是一个非常重要的区别,在许多情况下,使用IQueryable<T>可以避免从数据库返回太多行。另一个主要的例子是分页:如果你在IQueryable上使用Take和Skip,你将只得到请求的行数;在IEnumerable< t>上这样做会导致你所有的行都被载入内存。