如何在源集合为空时强制 LINQ Sum() 返回 0

Posted

技术标签:

【中文标题】如何在源集合为空时强制 LINQ Sum() 返回 0【英文标题】:How to force LINQ Sum() to return 0 while source collection is empty 【发布时间】:2013-07-09 17:23:20 【问题描述】:

基本上,当我执行以下查询时,如果没有匹配的线索,则以下查询会引发异常。在那种情况下,我宁愿让总和等于 0,而不是抛出异常。 这是否可能在查询本身 - 我的意思是而不是存储查询和检查 query.Any()

double earnings = db.Leads.Where(l => l.Date.Day == date.Day
                && l.Date.Month == date.Month
                && l.Date.Year == date.Year
                && l.Property.Type == ProtectedPropertyType.Password
                && l.Property.PropertyId == PropertyId).Sum(l => l.Amount);

【问题讨论】:

Where 不会返回 null,如果它没有找到任何记录,它将返回零项列表。有什么例外? 有什么异常? 我收到异常:转换为值类型“Int32”失败,因为具体化值为空。结果类型的泛型参数或查询必须使用可为空的类型。 @Stijn,不,你所做的仍然行不通。问题是SQL 的生成方式。 Amount 实际上不是null,它确实是一个围绕它如何处理零结果的问题。看看提供的答案。 您不应该将 double 用于美元金额! 即使是小数美元金额。永远不要在想要精确数量时使用 double。你的数据库列应该是decimal,你的代码应该使用decimal。忘记你曾经在你的编程生涯中知道floatdouble,直到有人告诉你使用它们,用于统计或恒星亮度或随机过程或电子电荷的结果!在那之前,你做错了 【参考方案1】:

尝试将您的查询更改为:

db.Leads.Where(l => l.Date.Day == date.Day
            && l.Date.Month == date.Month
            && l.Date.Year == date.Year
            && l.Property.Type == ProtectedPropertyType.Password
            && l.Property.PropertyId == PropertyId)
         .Select(l => l.Amount)
         .DefaultIfEmpty(0)
         .Sum();

这样,您的查询将只选择Amount 字段。如果集合为空,则返回一个值为0 的元素,然后应用总和。

【讨论】:

它确实可以解决问题,但它不会首先选择 Amount 值列表,然后在服务器端而不是在数据库端选择 Sum 它们吗? imo 2kay 的解决方案更优化,至少在语义上更正确。 @MaksimVI EF 将在第一次实现时生成查询,此时 IQueryable<T> 链停止(通常在您调用 ToListAsEnumerable 等时,在这种情况下为 Sum )。 Sum 是 EF Queryable Provider 已知和处理的方法,将生成相关的 SQL 语句。 @SimonBelanger 我的立场是正确的,总和是在 DB 端进行的,但它是在首先选择 Amounts 的子查询上进行的。基本上查询是SELECT SUM(a.Amount) FROM (SELECT Amount FROM Leads WHERE ...) AS a 而不仅仅是SELECT SUM(Amount) FROM Leads。此外,子查询还有一个额外的空检查和一个与单行表的奇怪外部连接。 没有显着的性能差异,它可能已经过优化,但我仍然认为其他解决方案看起来更干净。 请注意,许多 LINQ 提供程序不支持 DefaultIfEmpty,因此在这些情况下使用它之前,您需要输入 ToList() 或类似的东西,以便应用它在 LINQ to Objects 场景中。【参考方案2】:

我更喜欢使用另一种技巧:

double earnings = db.Leads.Where(l => l.Date.Day == date.Day
                                      && l.Date.Month == date.Month
                                      && l.Date.Year == date.Year
                                      && l.Property.Type == ProtectedPropertyType.Password
                                      && l.Property.PropertyId == PropertyId)
                          .Sum(l => (double?) l.Amount) ?? 0;

【讨论】:

当使用 Linq to SQL 时,这会生成比接受的答案更短的 SQL 代码 这是正确答案。所有其他人都失败了。首先转换为 nullable,然后将最终结果与 null 进行比较。 这比 Linq To EF 的公认答案要好得多。对我来说,生成的 SQL 的性能比 DefaultIfEmpty 好 3.8 倍。 这要快得多。 我不会将其称为 hack,因为这正是 nullables 的设计用途,即显示无数据和默认值之间的差异【参考方案3】:

试试这个,它更短:

db.Leads.Where(..).Aggregate(0, (i, lead) => i + lead.Amount);

【讨论】:

这样能避免异常吗? 您能详细说明一下吗? 我在 C# 编程方面有 15 年以上的经验,但没有从第一眼看到这行代码。这是如何不编写代码的真实示例。 @GiorgiNakeuri Aggregate 可能是像我这样的老前辈们遇到的问题之一,即使我们发现了 LINQ。我认为这与大多数其他在 SQL 中具有同等功能的运算符有关,因此我们可以转移知识。那么聚合真的不那么可读,还是只是不熟悉?从理论的角度来看,它是这里的“正确”运算符。当然,您也可以转换为 Nullable<>s 并使用 Sum。我的看法是,鼓起勇气并习惯函数式运算符,下一代会知道并使用它们。很多。【参考方案4】:

这对我来说是胜利:

int Total = 0;
Total = (int)Db.Logins.Where(L => L.id == item.MyId).Sum(L => (int?)L.NumberOfLogins ?? 0);

在我的 LOGIN 表中,NUMBEROFLOGINS 字段中的一些值为 NULL,而另一些值为 INT 号。我在这里总结了一个公司(每个 Id)的所有用户的总 NUMBEROFLOGINS。

【讨论】:

【参考方案5】:

试试:

双倍收益 = db.Leads.Where(l => l.ShouldBeIncluded).Sum(l => (double?) l.Amount) ?? 0;

查询“SELECT SUM([Amount])”将为空列表返回 NULL。 但是,如果您使用 LINQ,它期望“Sum(l => l.Amount)”返回双精度,并且不允许您使用“??”运算符为空集合设置 0。

为了避免这种情况,您需要让 LINQ 期待“double?”。您可以通过投射“(double?)l.Amount”来做到这一点。

它不影响对 SQL 的查询,但它使 LINQ 对空集合起作用。

【讨论】:

【参考方案6】:
db.Leads.Where(l => l.Date.Day == date.Day
        && l.Date.Month == date.Month
        && l.Date.Year == date.Year
        && l.Property.Type == ProtectedPropertyType.Password
        && l.Property.PropertyId == PropertyId)
     .Select(l => l.Amount)
     .ToList()
     .Sum();

【讨论】:

请在答案中添加一些关于代码的信息 当我尝试不使用 ToList() 时出现错误,因为它没有返回任何内容。但是 ToList() 会生成空列表,并且当我执行 ToList().Sum() 时它不会给出任何错误。 如果你想要的只是总和,你可能不想在这里使用ToList。这会将整个结果集(在这种情况下,每条记录只返回 Amount)到内存中,然后返回 Sum() 该集。使用另一种通过 SQL Server 进行计算的解决方案要好得多。【参考方案7】:

另一种选择是强制转换值,如下面的代码所示

(decimal?)(x.TicketDetails.Sum(y => y.Cost *y.Qty)) ?? 0

【讨论】:

严格不回答问题这在查询本身中是否可能?它只是转换整个查询的结果。其他答案足以涵盖如何正确地做到这一点。

以上是关于如何在源集合为空时强制 LINQ Sum() 返回 0的主要内容,如果未能解决你的问题,请参考以下文章

结果为空时LINQ返回啥

当所需的文本框为空时,如何强制页面回发

当其中一种数据类型可以为空而另一种不能为空时,是不是可以使用 linq 进行匿名连接? [复制]

当值为空或为空时如何返回默认值

当值为空时获取计数

强制值为空时无法引发 VALUE_ERROR