Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Linq 为什么实体框架是';s AsEnumerable()是否从服务器下载所有数据?_Linq_Entity Framework - Fatal编程技术网

Linq 为什么实体框架是';s AsEnumerable()是否从服务器下载所有数据?

Linq 为什么实体框架是';s AsEnumerable()是否从服务器下载所有数据?,linq,entity-framework,Linq,Entity Framework,当使用AsEnumerable()时,EF下载所有结果行的原因是什么 我的意思是这个代码: context.Logs.AsEnumerable().Where(x => x.Id % 2 == 0).Take(100).ToList(); 将从表中下载所有行,然后将任何行传递到Where()方法,表中可能有数百万行 我想让它做的是,只下载足够收集100行满足Id%2==0条件的数据(很可能只有大约200行) EF不能像使用普通ADO.NET那样使用Read()方法SqlDataReade

当使用
AsEnumerable()
时,EF下载所有结果行的原因是什么

我的意思是这个代码:

context.Logs.AsEnumerable().Where(x => x.Id % 2 == 0).Take(100).ToList();
将从表中下载所有行,然后将任何行传递到
Where()
方法,表中可能有数百万行

我想让它做的是,只下载足够收集100行满足
Id%2==0
条件的数据(很可能只有大约200行)

EF不能像使用普通ADO.NET那样使用
Read()
方法
SqlDataReader
按需加载行并节省时间和带宽吗

我想它不这样工作是有原因的,我希望听到一个很好的论据来支持这个设计决策

注意:这是一个完全人为设计的示例,我知道通常不应该以这种方式使用EF,但我在一些现有代码中发现了这一点,我只是惊讶于我的假设被证明是不正确的。

AsEnumerable()
急切地加载
DbSet
日志

你可能想要像这样的东西

context.Logs.Where(x => x.Id % 2 == 0).AsEnumerable();
这里的想法是,在从数据库实际加载集合之前,先对集合应用谓词过滤器


英孚支持LINQ世界中令人印象深刻的一个子集。它会在幕后将漂亮的LINQ查询转换为SQL表达式。

我以前遇到过这个问题。 在调用linq函数之前不会执行上下文命令,因为您已经完成了

context.Logs.AsEnumerable() 
它假定您已经完成了查询,因此编译并返回所有行。 如果将此更改为:

context.Logs.Where(x => x.Id % 2 == 0).AsEnumerable() 
它将编译一个SQL语句,只获取id为modular 2的行。 如果你这样做了,也是一样

context.Logs.Where(x => x.Id % 2 == 0).Take(100).ToList();
这将创造一个声明,将获得前100名


我希望这会有所帮助。

实体框架和Linq使用延迟加载。这意味着(除其他外)他们在需要枚举结果之前不会运行查询:例如使用
ToList()
AsEnumerable()
,或者如果结果用作枚举器(例如在
foreach
中)

相反,它使用谓词构建查询,并返回
IQueryable
对象,以在实际返回结果之前进一步“预过滤”结果。例如,您可以找到更多信息。实体框架将根据您传递的谓词实际构建SQL查询

在您的示例中:

context.Logs.AsEnumerable().Where(x => x.Id % 2 == 0).Take(100).ToList();
从上下文中的Logs表中,它获取全部,返回一个带有结果的
IEnumerable
,然后过滤结果,取前100个,然后将结果列为
列表

另一方面,只需删除
AsEnumerable
即可解决您的问题:

context.Logs.Where(x => x.Id % 2 == 0).Take(100).ToList();
在这里,它将在结果上构建一个查询/过滤器,然后仅在执行
ToList()
之后,查询数据库

这还意味着您可以动态构建复杂查询,而无需在DB上实际运行它,直到结束,例如:

var logs = context.Logs.Where(a); // first filter
if (something) {
    logs = logs.Where(b); // second filter
}
var results = logs.Take(100).ToList(); // only here is the query actually executed
更新 正如你在评论中提到的,你似乎已经知道我刚才写了什么,只是想问一个原因


它甚至更简单:因为
AsEnumerable
将结果强制转换为另一种类型(在本例中,a
IQueryable
转换为
IEnumerable
),它必须首先转换所有结果行,因此必须首先获取数据。在本例中,它基本上是一个
ToList

LinQ to实体在进入枚举之前有一个由所有LinQ方法形成的存储表达式

当您使用AsEnumerable()和Where()时,如下所示:

Lambda (=>)
  Parameters
    Variable: x
  Body
    Equals (==)
      Modulo (%)
        PropertyAccess (.)
          Variable: x
          Property: Id
        Constant: 2
      Constant: 0
SELECT *
FROM "Logs"
WHERE "Logs"."Id" % 2 = 0;
SELECT *
FROM "Logs"
WHERE "Logs"."Id" % 2 = 0
LIMIT 100;
context.Logs.Where(…).AsEnumerable()

Where()知道前一个链调用有一个存储表达式,因此他将谓词附加到该表达式以进行延迟加载

如果调用此函数,则调用位置的重载不同:

context.Logs.AsEnumerable().Where(...)

在这里,Where()只知道他以前的方法是枚举(它可以是任何类型的“可枚举”集合),他可以应用条件的唯一方法是使用DbSet类的IEnumerable实现在集合上迭代,必须首先从数据库中检索记录。

我认为您不应该使用此选项:

context.Logs.AsEnumerable().Where(x => x.Id % 2 == 0).Take(100).ToList();
正确的做法是:

context.Logs.AsQueryable().Where(x => x.Id % 2 == 0).Take(100).ToList();
请在此处解释:


简短回答:不同行为的原因是,当您直接使用
IQueryable
时,可以为整个LINQ查询形成一个SQL查询;但是当您使用
IEnumerable
时,必须加载整个数据表

<强>长回答:< /强>考虑下面的代码。

context.Logs.Where(x => x.Id % 2 == 0)
context.log
的类型为
IQueryable
IQueryable。其中
将一个
表达式
作为谓词。
表达式
表示一个抽象语法树;也就是说,它不仅仅是您可以运行的代码。可以将其视为在运行时在内存中表示,如下所示:

Lambda (=>)
  Parameters
    Variable: x
  Body
    Equals (==)
      Modulo (%)
        PropertyAccess (.)
          Variable: x
          Property: Id
        Constant: 2
      Constant: 0
SELECT *
FROM "Logs"
WHERE "Logs"."Id" % 2 = 0;
SELECT *
FROM "Logs"
WHERE "Logs"."Id" % 2 = 0
LIMIT 100;
LINQ to Entities引擎可以采用
context.Logs.Where(x=>x.Id%2==0)
并机械地将其转换为如下所示的SQL查询:

Lambda (=>)
  Parameters
    Variable: x
  Body
    Equals (==)
      Modulo (%)
        PropertyAccess (.)
          Variable: x
          Property: Id
        Constant: 2
      Constant: 0
SELECT *
FROM "Logs"
WHERE "Logs"."Id" % 2 = 0;
SELECT *
FROM "Logs"
WHERE "Logs"."Id" % 2 = 0
LIMIT 100;
如果将代码更改为
context.Logs.Where(x=>x.Id%2==0).Take(100)
,则SQL查询将变成如下所示:

Lambda (=>)
  Parameters
    Variable: x
  Body
    Equals (==)
      Modulo (%)
        PropertyAccess (.)
          Variable: x
          Property: Id
        Constant: 2
      Constant: 0
SELECT *
FROM "Logs"
WHERE "Logs"."Id" % 2 = 0;
SELECT *
FROM "Logs"
WHERE "Logs"."Id" % 2 = 0
LIMIT 100;
这完全是因为
IQueryable
上的LINQ扩展方法使用
Expression
而不仅仅是
Func


现在考虑<代码>上下文.Logas.asMeaveRable()。其中(x= > x.id % 2=0)。

IEnumerable.Where
扩展方法将
Func
作为谓词。这只是可运行的代码。无法对其进行分析以确定其结构;它不能用于形成SQL查询。

很明显,您知道原因