为什么实体框架的AsEnumerable()从服务器下载所有数据? [英] Why is Entity Framework's AsEnumerable() downloading all data from the server?
问题描述
使用AsEnumerable()
时EF下载所有结果行的解释是什么?
What is the explanation for EF downloading all result rows when AsEnumerable()
is used?
我的意思是这段代码:
context.Logs.AsEnumerable().Where(x => x.Id % 2 == 0).Take(100).ToList();
将从表中下载所有行,然后再将任何行传递给Where()
方法,并且表中可能有数百万行.
will download all the rows from the table before passing any row to the Where()
method and there could be millions of rows in the table.
我想做的是仅下载足以收集满足Id % 2 == 0
条件的100行(很可能只有200行).
What I would like it to do, is to download only enough to gather 100 rows that would satisfy the Id % 2 == 0
condition (most likely just around 200 rows).
EF是否可以像使用普通ADO.NET使用SqlDataReader
的Read()
方法那样按需加载行并节省时间和带宽?
Couldn't EF do on demand loading of rows like you can with plain ADO.NET using Read()
method of SqlDataReader
and save time and bandwidth?
我认为它不能像这样工作是有原因的,我想听到一个很好的论据来支持该设计决策.
I suppose that it does not work like that for a reason and I'd like to hear a good argument supporting that design decision.
注意:这是一个完全人为的示例,我知道通常您不应该以这种方式使用EF,但是我在某些现有代码中发现了这一点,只是感到惊讶,因为我的假设被证明是错误的.
NOTE: This is a completely contrived example and I know normally you should not use EF this way, but I found this in some existing code and was just surprised my assumptions turned out to be incorrect.
推荐答案
简而言之:不同行为的原因是,当直接使用IQueryable
时,单个SQL查询可以为您的整个LINQ查询组成;但是使用IEnumerable
时,必须加载整个数据表.
The short answer: The reason for the different behaviors is that, when you use IQueryable
directly, a single SQL query can be formed for your entire LINQ query; but when you use IEnumerable
, the entire table of data must be loaded.
长答案:考虑以下代码.
context.Logs.Where(x => x.Id % 2 == 0)
context.Logs
的类型为IQueryable<Log>
. IQueryable<Log>.Where
使用Expression<Func<Log, bool>>
作为谓词. Expression
代表抽象语法树;也就是说,它不仅仅是您可以运行的代码.可以认为它是在运行时在内存中表示的,就像这样:
context.Logs
is of type IQueryable<Log>
. IQueryable<Log>.Where
is taking an Expression<Func<Log, bool>>
as the predicate. The Expression
represents an abstract syntax tree; that is, it's more than just code you can run. Think of it as being represented in memory, at runtime, like this:
Lambda (=>)
Parameters
Variable: x
Body
Equals (==)
Modulo (%)
PropertyAccess (.)
Variable: x
Property: Id
Constant: 2
Constant: 0
LINQ-to-Entities引擎可以使用context.Logs.Where(x => x.Id % 2 == 0)
并将其机械地转换为如下所示的SQL查询:
The LINQ-to-Entities engine can take context.Logs.Where(x => x.Id % 2 == 0)
and mechanically convert it into a SQL query that looks something like this:
SELECT *
FROM "Logs"
WHERE "Logs"."Id" % 2 = 0;
如果将代码更改为context.Logs.Where(x => x.Id % 2 == 0).Take(100)
,则SQL查询将变为以下内容:
If you change your code to context.Logs.Where(x => x.Id % 2 == 0).Take(100)
, the SQL query becomes something like this:
SELECT *
FROM "Logs"
WHERE "Logs"."Id" % 2 = 0
LIMIT 100;
这完全是因为IQueryable
上的LINQ扩展方法使用Expression
而不是Func
.
This is entirely because the LINQ extension methods on IQueryable
use Expression
instead of just Func
.
现在考虑context.Logs.AsEnumerable().Where(x => x.Id % 2 == 0)
. IEnumerable<Log>.Where
扩展方法将Func<Log, bool>
用作谓词.那只是可运行的代码.无法对其进行分析以确定其结构;它不能用于形成SQL查询.
Now consider context.Logs.AsEnumerable().Where(x => x.Id % 2 == 0)
. The IEnumerable<Log>.Where
extension method is taking a Func<Log, bool>
as a predicate. That is only runnable code. It cannot be analyzed to determine its structure; it cannot be used to form a SQL query.
这篇关于为什么实体框架的AsEnumerable()从服务器下载所有数据?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!