如果可能有空集,如何进行Linq聚合?

我有一个Linq的Things集合,其中Thing有一个Amount (十进制)属性。

我正在尝试针对特定事物的子集对此进行聚合:

 var total = myThings.Sum(t => t.Amount); 

这很好用。 但后来我添加了一个条件,让我在结果中没有任何东西:

 var total = myThings.Where(t => t.OtherProperty == 123).Sum(t => t.Amount); 

而不是得到total = 0或null,我得到一个错误:

System.InvalidOperationException:无法将null值分配给类型为System.Decimal的成员,该成员是非可空值类型。

这真是令人讨厌,因为我没想到这种行为。 我本来期望总数为零,也许是null – 但肯定不会抛出exception!

我究竟做错了什么? 什么是解决方法/修复?

编辑 – 例子

感谢大家的评论。 这是一些代码,复制和粘贴(不简化)。 这是LinqToSql(也许这就是为什么你无法重现我的问题):

 var claims = Claim.Where(cl => cl.ID  cl.ClaimedAmount); // throws exception 

我可以使用针对Northwind的以下LINQPad查询重现您的问题:

 Employees.Where(e => e.EmployeeID == -999).Sum(e => e.EmployeeID) 

这里有两个问题:

  1. Sum()重载
  2. LINQ to SQL遵循SQL语义,而不是C#语义。

在SQL中, SUM(no rows)返回null ,而不是零。 但是,查询的类型推断会将decimal作为类型参数而不是decimal? 。 修复是帮助类型推断选择正确的类型,即:

 Employees.Where(e => e.EmployeeID == -999).Sum(e => (int?)e.EmployeeID) 

现在将使用正确的Sum()重载。

要获得不可为空的结果,需要将数量转换为可空类型,然后处理Sum返回null的情况。

 decimal total = myThings.Sum(t => (decimal?)t.Amount) ?? 0; 

还有另一个专门讨论(ir)理由的问题 。

它会引发exception,因为组合的sql查询的结果为null,并且无法将其分配给decimal var。 如果你执行了以下操作,那么你的变量将为null(我假设ClaimedAmount是十进制的):

 var claims = Claim.Where(cl => cl.ID < 0); var count = claims.Count(); // count=0 var sum = claims.Sum(cl => cl.ClaimedAmount as decimal?); 

那么你应该得到你想要的function。

您还可以在where语句的位置执行ToList(),然后总和将返回0,但这将与其他地方有关LINQ聚合的内容相违背。

如果t具有类似’HasValue’的属性,那么我将表达式更改为:

 var total = myThings.Where(t => (t.HasValue) && (t.OtherProperty == 123)).Sum(t => t.Amount);