当可能存在空集时如何进行 Linq 聚合?

发布于 2024-08-24 23:05:02 字数 864 浏览 5 评论 0原文

我有一个 Things 的 Linq 集合,其中 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:无法将空值分配给 System.Decimal 类型的成员,它是不可为 null 的值类型。

这真的很令人讨厌,因为我没想到会有这种行为。我本来期望总计为零,也许为空 - 但肯定不会抛出异常!

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

编辑 - 示例

感谢大家的评论。这是一些复制并粘贴的代码(未简化)。它是 LinqToSql (也许这就是您无法重现我的问题的原因):

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

I have a Linq collection of Things, where Thing has an Amount (decimal) property.

I'm trying to do an aggregate on this for a certain subset of Things:

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

and that works nicely. But then I added a condition that left me with no Things in the result:

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

And instead of getting total = 0 or null, I get an error:

System.InvalidOperationException: The null value cannot be assigned to
a member with type System.Decimal which is a non-nullable value type.

That is really nasty, because I didn't expect that behavior. I would have expected total to be zero, maybe null - but certainly not to throw an exception!

What am I doing wrong? What's the workaround/fix?

EDIT - example

Thanks to all for your comments. Here's some code, copied and pasted (not simplified). It's LinqToSql (perhaps that's why you couldn't reproduce my problem):

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

枉心 2024-08-31 23:05:02

我可以使用以下针对 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() 重载。

I can reproduce your problem with the following LINQPad query against Northwind:

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

There are two issues here:

  1. Sum() is overloaded
  2. LINQ to SQL follows SQL semantics, not C# semantics.

In SQL, SUM(no rows) returns null, not zero. However, the type inference for your query gives you decimal as the type parameter, instead of decimal?. The fix is to help type inference select the correct type, i.e.:

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

Now the correct Sum() overload will be used.

迷荒 2024-08-31 23:05:02

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

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

还有另一个问题专门讨论(ir)基本原理

To get a non-nullable result, you need to cast the amount to a nullable type, and then handle the case of Sum returning null.

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

There's another question devoted to the (ir)rationale.

若言繁花未落 2024-08-31 23:05:02

它会抛出异常,因为组合 sql 查询的结果为 null,并且不能将其分配给十进制 var。如果您执行以下操作,那么您的变量将为空(我假设 ClaimedAmount 是十进制):

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

那么您应该获得您想要的功能。

您还可以在 where 语句处执行 ToList(),然后总和将返回 0,但这将与其他地方关于 LINQ 聚合的说法相冲突。

it throws an exception because the result of the combined sql query is null and this cant be assigned to the decimal var. If you did the following then your variable would be null (I assume ClaimedAmount is decimal):

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

then you should get the functionality you desire.

You could also do ToList() at the point of the where statement and then the sum would return 0 but that would fall foul of what has been said elsewhere about LINQ aggregates.

深海不蓝 2024-08-31 23:05:02

如果 t 具有像“HasValue”这样的属性,那么我会将表达式更改为:

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

If t has a property like a 'HasValue', then I would change the expression to:

var total = 
     myThings.Where(t => (t.HasValue) && (t.OtherProperty == 123)).Sum(t => t.Amount); 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文