Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/entity-framework/4.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.Where()”中;提供不同于“的结果”;IEnumerable.Where();在这个查询中?_C#_Entity Framework_Linq_Entity Framework Core - Fatal编程技术网

C# “为什么?”;IQueryable.Where()”中;提供不同于“的结果”;IEnumerable.Where();在这个查询中?

C# “为什么?”;IQueryable.Where()”中;提供不同于“的结果”;IEnumerable.Where();在这个查询中?,c#,entity-framework,linq,entity-framework-core,C#,Entity Framework,Linq,Entity Framework Core,我试图使用linq从EF Core数据集中筛选一些行,但我不明白为什么筛选IEnumerable和筛选IQueryable会得到不同的结果 var query = _db.Jobs .IsDelivered() .Include(a => a.JobExtras) .Include(a => a.Tips) .Include(a => a.Payments)

我试图使用linq从EF Core数据集中筛选一些行,但我不明白为什么筛选IEnumerable和筛选IQueryable会得到不同的结果

        var query = _db.Jobs
            .IsDelivered()
            .Include(a => a.JobExtras)
            .Include(a => a.Tips)
            .Include(a => a.Payments)
            .HasPayments();

        var query1 = query
            .ToList()
            .Where(a => a.Payments.Sum(b => b.Amount)
                         - a.Price.Value
                         - a.Discount
                         - a.JobExtras.Sum(c => c.Price)
                         - a.Tips.Sum(d => d.Amount)
                         > 0);

        var query2 = query
           .Where(a => a.Payments.Sum(b => b.Amount)
                         - a.Price.Value
                         - a.Discount
                         - a.JobExtras.Sum(c => c.Price)
                         - a.Tips.Sum(d => d.Amount)
                         > 0);

        Debug.WriteLine($"Record Count[Before Where Clause]: {query.Count()}");
        Debug.WriteLine($"Record Count[ToList() version]: {query1.Count()}");
        Debug.WriteLine($"Record Count[w/out ToList()]: {query2.Count()}");
以下是输出:

Record Count[Before Where Clause]: 8379
Record Count[ToList() version]: 5921
Record Count[w/out ToList()]: 0

为什么IEnumerable版本生成5921条记录,而IQueryable版本生成0条记录?

第一个查询在内存中使用.NET类型和
Enumerable.Sum()执行。跳过传递到
Sum
中的任何空值,因此您的一些数据正在通过
>0
测试

第二个查询在数据库中执行,并使用SQL的
SUM
。如果
Where
子句中表达式的任何部分包含null,则该表达式的计算结果将为null。获取该表达式并对其进行比较(
null>0
)在SQL中的计算结果始终为false,因此所有行都将被筛选

您的第二个查询可能是可修复的。我没有证据证明这一点,因为我不知道您的模型(即,什么是可空的或不可空的),也不确定EF是否可以转换这一点:

.Where(a => a.Payments.Where(x => x != null).Sum(b => b.Amount)
     - a.Price.Value
     - a.Discount
     - a.JobExtras.Where(x => x != null).Sum(c => c.Price)
     - a.Tips.Where(x => x != null).Sum(d => d.Amount)
     > 0);

因此,根据Kit和madreflection提供的信息:

我刚刚为DB重新设定了种子,并确保在3个子表中有行:Payments、JobExtras和Tips

瞧,这个查询既适用于IQueryable版本,也适用于IEnumerable版本。我得到的行数相同

因此,总而言之,问题的答案是IQueryable查询的问题是,如果数据库中没有我试图求和的对象的子行,SQL将返回NULL而不是0

谢谢你的帮助

这种行为很有趣

那么现在,有没有办法修复IQueryable查询以应对子对象(小费、付款、临时工)可能是“空集”(无行)的可能性

============= 工作解决方案(归功于@Steve Py&@madreflection)


好吧,我有几件事要尝试,但我读到你发现问题在于失踪的孩子

我做了一个快速测试,以下内容在您的情况下应该有效:

  var query2 = query
       .Where(a => a.Payments.Sum(b => b.Amount)
                     - a.Price.Value
                     - a.Discount
                     - (a.JobExtras.Any() ? a.JobExtras.Sum(c => c.Price) : 0)
                     - (a.Tips.Any() ? a.Tips.Sum(d => d.Amount) : 0)
                     > 0);

将Jobs.Price视为查询中唯一可为空的值,则Iqueryable linq查询可以重写如下:

.Where(a => a.Payments.Sum(b => b.Amount)
     - a.Price.Value
     - a.Discount
     - a.JobExtras.Sum(c => c.Price ?? 0)
     - a.Tips.Sum(d => d.Amount)
     > 0);

您的计算中是否有
NULL
s列?
NULL
上的任何数学或逻辑都将产生
NULL
,包括
NULL>0
->
NULL
,这不是真的,因此在功能上等同于假。我想可能也是这样,所以我删除了唯一一个可以为NULL的术语“Price”,但结果相同。另外,如果是这样的话,这不应该像query2那样影响query1吗?不。当您调用
ToList
,它会执行查询,此时您正在对.NET类型/值进行操作
Sum
将忽略
null
而不是产生
null
Count
将对
null
项进行计数而不是忽略它。如果您的源数据没有
NULL
s,这将是毫无意义的,但我认为您可能会在没有可聚合项的情况下得到
NULL
。谢谢您的思路。madreflection正确地提到NULL是问题,但不是正确的领域。我认为问题不是一个可为空的字段,而是一个可为空的集合。Iqueryable是SQL,SQL处理的是“集合”,而不是对象。因此,当您尝试执行Tips.Sum(诸如此类)时,如果没有Tips行(有几个作业对象没有“子”tip行),SQL将返回“NULL”,其中as.NET返回一个“0”,使得整个查询无法通过SQL使用。想法?如果我上次的评论是正确的,这意味着您尝试的“修复”也不会起作用,因为Tips。如果DB中有0个Tips行,其中(x=>x!=null)仍将导致null。我将此标记为正确答案,因为它让我找到解决方案。我在下面创建了一个带有最终工作解决方案的答案。谢谢大家的帮助。@FrankBusalacchi:FWIW,我在第二条评论中提到了集合,当时我说“没有要聚合的项目”。@madreflection——是的,我同意你也提到了,你也是第一个回应的人!这就是为什么我在回答中说,根据你们双方提供的信息。。这也是我对你的评论投赞成票的原因。我感谢你在我的问题上的帮助和努力!感谢您,也感谢所有花时间阅读本文并提供想法、建议和解决方案的人。所有对象中唯一可为空的字段是Jobs.Price。尝试做b=>Amount??0甚至不会编译,因为Amount不可为null。不知何故,当数据库中没有Payment/Tips/jobetras行时,表达式a.jobetras.Sum(c=>c.Price)必须计算为0而不是null。您可以
a.Payments.Sum(b=>(十进制?)b.Amount吗??0M)
相反,正如Kit所提到的,当IQueryable.Sum()呈现一个空集合时,它的行为就是IQueryable.Sum()的行为。当集合为空时,IQueryable.Sum()=NULL。当集合为空时,IEnumerable.Sum()=0。SQL纯粹主义者可能会认为这是正确的,但在我看来,IQueryable.Sum()和IEnumerable.Sum应该具有一致的行为,要么都返回Null,要么都返回0;这一切都有道理。我只是想简化你的表达。
Sum
的结果并不总是
0
。对于可为null的值类型,它是
null
,您可以将其合并到
0
。将
b.Amount
(例如)强制转换为null将消除对
Any
的调用,从而消除三元运算符的需要。从理论上讲,您必须测试它以确认它能够生成更好的SQL。@madreflection:我刚刚交换了您的建议,它也确实起了作用。我当然更喜欢三元结构,虽然我还没有研究过,但我同意y
.Where(a => a.Payments.Sum(b => b.Amount)
     - a.Price.Value
     - a.Discount
     - a.JobExtras.Sum(c => c.Price ?? 0)
     - a.Tips.Sum(d => d.Amount)
     > 0);