返回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;
两者都将被延迟执行,何时应该优先于其中一个?
一般来说,您希望保留查询的原始静态类型,直到有必要。
出于这个原因,你可以将你的变量定义为'var',而不是IQueryable<>或IEnumerable<>,你会知道你没有改变类型。
如果您开始使用IQueryable<>,通常希望将其保留为IQueryable<>,直到有一些令人信服的理由更改它。这样做的原因是您希望为查询处理器提供尽可能多的信息。例如,如果你只打算使用10个结果(你已经调用Take(10)),那么你想让SQL Server知道这一点,这样它就可以优化它的查询计划,只向你发送你将使用的数据。
A compelling reason to change the type from IQueryable<> to IEnumerable<> might be that you are calling some extension function that the implementation of IQueryable<> in your particular object either cannot handle or handles inefficiently. In that case, you might wish to convert the type to IEnumerable<> (by assigning to a variable of type IEnumerable<> or by using the AsEnumerable extension method for example) so that the extension functions you call end up being the ones in the Enumerable class instead of the Queryable class.
是的,两者都使用延迟执行。让我们使用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不会给你带来任何好处。