【问题标题】:Sum() Returns null in Entity Framework QuerySum() 在实体框架查询中返回 null
【发布时间】:2015-01-09 18:29:31
【问题描述】:

我有一个包含这些行的大型实体框架查询。

var programs = from p in Repository.Query<Program>()
               where p.OfficeId == CurrentOffice.Id
               let totalCharges = p.ProgramBillings.Where(b => b.Amount > 0 && b.DeletedDate == null).Select(b => b.Amount).Sum()
               let totalCredits = p.ProgramBillings.Where(b => b.Amount < 0 && b.DeletedDate == null).Select(b => -b.Amount).Sum()
               let billingBalance = (totalCharges - totalCredits)

当我实现数据时,我收到以下错误:

转换为值类型“十进制”失败,因为具体化值为空。结果类型的泛型参数或查询必须使用可为空的类型。

如果我按如下方式更改我的查询(在两种类型转换中添加),错误就会消失。

var programs = from p in Repository.Query<Program>()
               where p.OfficeId == CurrentOffice.Id
               let totalCharges = (decimal?)p.ProgramBillings.Where(b => b.Amount > 0 && b.DeletedDate == null).Select(b => b.Amount).Sum()
               let totalCredits = (decimal?)p.ProgramBillings.Where(b => b.Amount < 0 && b.DeletedDate == null).Select(b => -b.Amount).Sum()
               let billingBalance = (totalCharges - totalCredits)

我不明白这一点。 ProgramBilling.Amount 是不可为空的十进制。如果我将鼠标悬停在Sum() 调用上,Intellisense 会说它返回十进制类型。而另外的测试证实,在我的第二个版本中,totalChargestotalCredits 对于ProgramBillings 没有数据的那些行都设置为空。

问题:

  1. 我知道Sum() 为空集合返回 0。在什么情况下这不是真的?

  2. 如果有时这不是真的,那么为什么当我将鼠标悬停在Sum() 上时,Intellisense 会显示它返回类型 Decimal 而不是 Decimal?看来 Intellisense 的理解和我一样。

编辑:

似乎一个简单的解决方法是执行Sum() ?? 0m 之类的操作。但这是非法的,给我错误:

运算符'??'不能应用于“十进制”和“十进制”类型的操作数

【问题讨论】:

  • 你看过这个帖子吗:stackoverflow.com/questions/17593371/…
  • @JonathanWood:“但我仍然对为什么这是必要的感到困惑”再次阅读链接问题的标题。这是必要的,因为集合是空的。如果不为空,您希望空集合的总和是多少?显然,0 并没有削减它,因为 0 是一个完全有效的总和值。
  • @DavidTansey:我确实看到了,但它没有提供关于我在问题末尾提出的任何一个问题的任何线索。这是我将根据需要采取的方法。只是想了解原因。
  • Select 之后,在Sum 之前添加DefaultIfEmpty(0),尝试接受的答案中的内容。现在,如果您有一个空集合,您将得到0,0 的总和当然是 0。
  • @MattBurland:正如我在别处指出的那样,我希望空集合的总和为 0。显然,Intellisense 和我一样认为 Sum() 永远不会返回 null。 Cleary 0 doesn't cut it since 0 is a perfectly valid value for a sum 好吧,如果 0 是一个完全有效的求和值,那么为什么 0 不削减呢?

标签: c# linq entity-framework


【解决方案1】:

我理解 Sum() 为空集合返回 0。这在什么情况下不成立?

当您不使用 LINQ to 对象时,就像这里的情况一样。在这里,您有一个查询提供程序,正在将此查询转换为 SQL。 SQL 操作对其 SUM 运算符有不同的语义。

如果有时这不是真的,那么为什么当我将鼠标悬停在 Sum() 上时,Intellisense 会显示它返回类型 Decimal 而不是 Decimal?看来 Intellisense 的理解和我一样。

C# LINQ SUM 运算符不返回可为空的值;它需要有一个非空值,但 SQL SUM 运算符有不同的语义,它在对空集求和时返回 null,而不是 0null 值是在 C# 需要非空值的上下文中提供的这一事实是一切都失败的全部原因。如果此处的 C# LINQ SUM 运算符返回一个可为空的值,那么 null 可以毫无问题地返回。

导致此错误的是 C# 运算符和它用来表示的 SQL 运算符之间的差异。

【讨论】:

  • 感谢您的解释。我认为这与 C# 和 SQL 语义之间的差异有关,但编译器在这里假定 C# 语义似乎是一个缺点。如果没有,我可以简单地使用合并运算符将 null 返回值强制为零。
  • @JonathanWood LINQ 无法支持可以在任何地方查询的每一件事的语义,永远。如果它尝试过,将会有太多东西使它几乎无法使用。它需要就支持什么类型的操作、不支持什么以及应该支持什么语义做出某些决定。然后,查询提供程序负责将 LINQ 中描述的操作映射到它正在查询的任何内容。这种情况的现实是,一切都不会 1:1 映射。
  • 我明白这一点,但在那种情况下,它似乎需要不太严格的类型结构。无论如何,我很难找到对此的解释。这个问题引起了相当多的关注。再次感谢。
【解决方案2】:

当集合为空时,我在我的一个 EF 查询中遇到了同样的问题,对此的一种快速解决方法是强制转换为可为空的小数:

var total = db.PaiementSet.Sum(o => (Decimal?)o.amount) ?? 0M;

希望对你有帮助。

【讨论】:

    【解决方案3】:

    在 .Sum 之前添加一个 DefaultIfEmpty(0.0M)

    【讨论】:

      猜你喜欢
      • 2021-06-26
      • 1970-01-01
      • 2018-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-16
      相关资源
      最近更新 更多