Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/303.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

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
C# 为什么不应该混合IQueryable和IEnumerable查询?_C#_Linq - Fatal编程技术网

C# 为什么不应该混合IQueryable和IEnumerable查询?

C# 为什么不应该混合IQueryable和IEnumerable查询?,c#,linq,C#,Linq,在linq中执行查询时,建议不要将iqeuryable和ienumerable部分组合在一起。这仅仅是一个性能问题还是什么 我想澄清一下。有时这样做是不可行的(就像@Harald Coppoolse在他的回答中解释的那样)。但有时是可行的,但我不确定表演浪漫会发生什么。 假设这个sudo代码: 'from e in someIEnumerable join q in someIQueryable on e.reference equals q.ID' 那么这个查询会发生什么呢?在LinQ到实体

在linq中执行查询时,建议不要将
iqeuryable
ienumerable
部分组合在一起。这仅仅是一个性能问题还是什么

我想澄清一下。有时这样做是不可行的(就像@Harald Coppoolse在他的回答中解释的那样)。但有时是可行的,但我不确定表演浪漫会发生什么。 假设这个sudo代码:

'from e in someIEnumerable
join q in someIQueryable on e.reference equals q.ID'

那么这个查询会发生什么呢?

在LinQ到实体的上下文中

IQueryable
扩展了
IEnumerable
,它只是一个查询表达式。这意味着它不会解析实体数据存储中的数据,例如,直到
.ToList()
FirstOrDefault()

IEnumerable
提供
GetEnumerator()
方法,允许迭代已解析的集合

因此,在数据访问层之前,您应该使用
IQueryable
,然后在已经获得数据时使用
IEnumerable

使用适当的接口是一种不获取所有不需要的数据的方法,并且只对需要处理的数据进行处理(例如迭代)


根据Jonsket answer编辑

要决定是否使用IEnumerable继续IQueryable LINQ语句,必须了解两者之间的差异

实现
IEnumerable
的类的对象是一个表示序列的对象。它保存其中的所有内容以获取序列的第一个元素,一旦获得了一个元素,如果有下一个元素,就可以请求下一个元素

如果您使用
IEnumerable.GetEnumerator()
IEnumerator.MoveNext()
,或者在使用诸如
foreach
ToList()
ToDictionary()
FirstOrDefault()
Sum()
Count()
Any()等函数时,将显式启动此枚举
,等等。如果您调查,您会发现他们在内心深处调用
GetEnumator()
MoveNext()

虽然实现
IQueryable
的类的对象也表示一个序列,但它不必知道如何枚举这个序列。它包含一个
表达式和一个
提供程序
表达式
是被查询数据的一种非常通用的形式,
提供程序
知道谁必须执行查询(通常是数据库管理系统)和用于与执行器通信的语言(通常类似于SQL)

IQueryable
对象与同样返回
IQueryable
的方法连接起来,只会更改
表达式;不会联系数据库

调用
Queryable.GetEnumerator()
开始枚举Queryable时,
Expression
将发送给
提供者
,后者将此
表达式
转换为SQL并查询执行器。返回的数据将具体化为IEnumerable序列。
GetEnumerator()调用
,并返回返回的枚举数,因此您可以调用
MoveNext()
Current
,就好像您正在枚举一个
IEnumerable
一样

由于必须将
表达式
转换为SQL,因此不能像使用
IEnumerable
那样使用
IQueryable
完成所有操作

以下内容适用于
IEnumerable

double CalculateValueAddedTax(Price p) {...}

IEnumerable<OrderLine> orderLines = ...
decimal totalValueAddedTax = orderLines
    .Where(orderLine => orderLine.HasValueAddedTax)
    .Select(orderLine => CaculateValueAddedTax(orderLine.Price))
    .Sum();
虽然这会起作用,但如果您只打算使用其中的一些数据(例如,如果您使用
FirstOrDefault()

这有点取决于
提供者
可计算的
将做什么,但智能
提供者
将“每页”查询数据,这样就不会获取所有数百万查询数据,而只获取部分数据,比如说25。如果使用
FirstOrDefault
,则会有一些数据是免费获取的,但至少不会获取数百万。只要枚举第26个元素,就会获取下一个页面。页面大小是在获取太多数据和经常执行查询

因此,
AsEnumerable
AsQueryable
之间的主要区别在于,可枚举函数将由本地进程执行:您可以调用的每个函数都可以由可枚举函数执行。
AsQueryable
由外部进程执行。您调用的每个函数都必须翻译成t语言外部进程理解的内容,从而限制了您可以在查询中使用的函数

编译器无法检测外部进程使用的语言,也不会抱怨。如果使用不支持的函数,则会出现异常

回到你的问题:我应该使用可计算/可查询的

可计算 上面的例子表明,有时您必须在继续LINQ之前向本地进程查询部分数据。明智的做法是使用AsEnumerable

数据库查询中较慢的部分之一是将所选数据传输到本地进程。因此,如果您决定使用可计算的,请尝试仅传输本地需要的数据:如果您只想处理
价格

此外,看看是否可以将本地函数更改为IQueryable

IQueryable<decimal> CalculateValueAddedTaxes(this IQueryable<Price> prices)
{
    return prices.Select(price => price.VatPercentage * prive.Value);
}
易怒的 如果您有一个本地可枚举项,
AsQueryable
不会突然将数据传输到数据库。它会创建一个
表达式
和一个
提供程序
。这个
表达式
只是对输入数据的函数调用。就像任何查询表一样,当您开始枚举时,
表达式
发送到
Prov
IQueryable<OrderLine> orderLines = ...
IEnumerable<Price> pricesWithValueAddedTaxes = orderLines
    .Where(orderLine => orderLine.HasValueAddedTax)
    .Select(orderLine => orderLine.Price)
    .ToList();
decimal totalValueAddedTax = pricesWithValueAddedTax
    .Select(price => CaculateValueAddedTax(price))
    .Sum();
IQueryable<Human> queryAmericans = myDbContext.Humans
    .Where(human => human.Country == "USA")
    .OrderByDescending(human => human.Age);
List<Human> americans = queryAmericans.ToList();
var oldestSpecialAmerican = americans
    .Where(american => american.IsSpecial())
    .FirstOrDefault();
Price result = orderLines
    .Where(orderLine => orderLine.HasValueAddedTax)
    .Select(orderLine => orderLine.Price)
    .OrderByDescending(price => price.Value)
    .AsEnumerable()
    .Select(price => CaculateValueAddedTax(price))
    .FirstOrDefault();
IQueryable<decimal> CalculateValueAddedTaxes(this IQueryable<Price> prices)
{
    return prices.Select(price => price.VatPercentage * prive.Value);
}
var result = orderLines
    .Where(orderLine => orderLine.HasValueAddedTax)
    .Select(orderLine => orderLine.Price)
    .OrderByDescending(price => price.Value)
    .CalculateValueAddedTaxes()
    .FirstOrDefault();
IQueryable<decimal> CalculateValueAddedTaxes(this IQueryable<Price> prices) {...}

Order newOrder = new Order() {...};      // A local object
newOrder.TotalVAT = newOrder.OrderLines  // A local sequence of OrderLInes
     .AsQueryable()                      // still Local, but now as IQueryable
     .CalculateValueAddedTaxex()         // so you can call this function
     .Sum();