协慌网

登录 贡献 社区

返回 IEnumerable <T> 与 IQueryable <T>

返回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;

两者都会延迟执行,何时应该优先于另一个?

答案

是的,两者都会给你延期执行

区别在于IQueryable<T>是允许 LINQ-to-SQL(LINQ.-to-anything 真正)工作的接口。因此,如果您在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上使用TakeSkip ,你将只获得所请求的行数; 在IEnumerable<T>上执行此操作将导致所有行都加载到内存中。

最好的答案是好的,但它没有提到解释两个接口 “如何” 不同的表达式树。基本上,有两组相同的 LINQ 扩展。 Where()Sum()Count()FirstOrDefault()等都有两个版本:一个接受函数,另一个接受表达式。

  • IEnumerable版本签名是: Where(Func<Customer, bool> predicate)

  • IQueryable版本签名是: Where(Expression<Func<Customer, bool>> predicate)

您可能已经使用了两者而没有意识到它,因为两者都使用相同的语法调用:

例如Where(x => x.City == "<City>")适用于IEnumerableIQueryable

  • IEnumerable集合上使用Where()时,编译器将已编译的函数传递给Where()

  • IQueryable集合上使用Where()时,编译器将表达式树传递给Where() 。表达式树就像反射系统,但代码。编译器将您的代码转换为数据结构,该数据结构以易于理解的格式描述您的代码所执行的操作。

为什么要打扰这个表达式树呢?我只想要Where()来过滤我的数据。主要原因是 EF 和 Linq2SQL ORM 都可以将表达式树直接转换为 SQL,您的代码执行速度会快得多。

哦,这听起来像是一个免费的性能提升,我应该在这种情况下使用AsQueryable()吗?不, IQueryable仅在底层数据提供者可以使用它时才有用。将常规List等内容转换为IQueryable不会给您带来任何好处。

是的,都使用延期执行。让我们用 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运行此代码块大约需要四秒钟。

IQueryable 有一个名为Expression的属性,它存储一个树表达式,当我们在我们的示例中使用result (称为延迟执行)时,它开始被创建,最后这个表达式将被转换为 SQL 查询以在数据库引擎上运行。