【问题标题】:Handling null results with the LINQ Average() method使用 LINQ Average() 方法处理空结果
【发布时间】:2009-08-17 10:07:37
【问题描述】:

我是LINQ 的新手,我正在尝试创建一些从表格到图表的数据点。该表中三个重要的字段是id、时间和值。我正在编写一个查询来获取所选 id 在设定时间内的平均值。我写的 LINQ 如下:

var value = (from t in _table
             where t.Id == id
                 && t.Time >= intervalStartTime
                 && t.Time <= intervalEndTime
             select t.Value).Average();

但是,这会在运行时崩溃:

"空值不能赋值给 System.Decimal 类型的成员 这是一个不可为空的值类型.."

在某些时间间隔没有数据,因此 SQL LINQ 生成返回 null,我希望将其 COALESCED 为 0,但会导致应用程序崩溃。有没有办法编写这个 LINQ 查询来正确处理这个问题?

表定义让事情更清楚:

[Serializable]
[Table(Name = "ExampleTable")]
public class ExampleTable
{
    [Column(Name = "Id")]
    public int Id { get; set; }

    [Column(Name = "Time")]
    public DateTime Time { get; set; }

    [Column(Name = "Value")]
    public int Value{ get; set; }
}

【问题讨论】:

    标签: c# .net linq


    【解决方案1】:

    我想你想要

    var value = (from t in _table
                 where t.Id == id
                    && t.Time >= intervalStartTime
                    && t.Time <= intervalEndTime
                 select (int?)t.Value).Average()
    

    这样,你会得到一个double? 回复,而没有(int?) 演员你需要得到一个double 回复,它不能是null

    这是因为签名

    double Enumerable.Average(IEnumerable<int> source)
    double? Enumerable.Average(IEnumerable<int?> source)
    

    现在,要获得平均值 0 而不是 null,您需要将合并运算符放在末尾​​p>

    var value = (from t in _table
                 where t.Id == id
                    && t.Time >= intervalStartTime
                    && t.Time <= intervalEndTime
                 select (int?)t.Value).Average() ?? 0.0;
    

    恕我直言,这是 Enumerable/Queryable 类的一个非常糟糕的设计;为什么Average(IEnumerable&lt;int&gt;)不能返回double?,为什么只能返回Average(IEnumerable&lt;int?&gt;)

    【讨论】:

    • 只是为了帮助其他人解决这个问题:如果查询不返回任何行,则没有任何帮助。您只需要完全避免运行查询。至少这是我的经验。
    • @horace:这根本不是真的。当查询不匹配任何行只要聚合结果可以为空时,Average、Min、Max、Sum 都有效。正如您在上面的查询中看到的那样,当要聚合的表达式不是可空类型时,您需要强制转换以强制可空性。 (在极少数情况下它仍然无法正常工作,您可能正在处理一个不稳定的 LINQ 提供程序。这不是 LINQ 的错,而且我所知道的没有一个提供程序是这样搞砸的。)
    【解决方案2】:

    编辑:完全改变:)

    好的,这个怎么样:

    var value = (from t in _table
                 where t.Id == id
                     && t.Time >= intervalStartTime
                     && t.Time <= intervalEndTime
                 select t.Value).DefaultIfEmpty().Average()
    

    我相信这逻辑上是您想要的 - 将 {} 更改为 {0},这样可以实现所有平均值。我不知道它是否会在 SQL 方面做你想要的。

    【讨论】:

    • 谢谢,但还是不开心。引发“无法格式化节点 'OptionalValue' 以作为 SQL 执行。”
    • +1 DefaultIfEmpty() 解决了如果 linq 的 where 部分将集合减少为无行该怎么办
    【解决方案3】:

    编辑:全部返工

    先尝试将值转换为可为空的

    var value = (from t in _table
             where t.Id == id
                 && t.Time >= intervalStartTime
                 && t.Time <= intervalEndTime
             select ((int?)t.Value) ?? 0).Average()
    

    【讨论】:

    • 选择 t.Value ?? 0 不会编译为 t.Value 是不可为空的 int。
    • 是的,编译器告诉我左操作数永远不会为空,因为值不可为空
    • 您确定括号等正确吗?强制转换为 nullable 是合法的,所以 Value 应该已经被评估了......
    【解决方案4】:

    试试下面的。它只会跳过查询返回的所有空项。

    var value = (from t in _table
                 where t != null
                 where t.Id == id
                     && t.Time >= intervalStartTime
                     && t.Time <= intervalEndTime
                 select t.Value).Average();
    

    如果您想明确地将 null 项视为零,那么简单地使用 条件运算符 就可以完成这项工作:

    var value = (from t in _table
                 where t == null ||
                     (t.Id == id
                     && t.Time >= intervalStartTime
                     && t.Time <= intervalEndTime)
                 select t == null ? 0 : t.Value).Average();
    

    【讨论】:

    • t 怎么可能为空,并且介于intervalStartTimeintervalEndTime 之间?我认为他的问题是在某些开始和结束时间之间没有记录,而不是表中有空值。
    • 哦,t.Value vs. t.Time - 我明白了。
    • 感谢您的建议,但仍然抛出相同的异常。
    • @Dominic:第一个例子不应该通过任何异常。我现在更新了第二个示例来修复它(确实,它不完整)。
    • 再次抱歉。两个都试过了。他们仍然抛出“空值不能分配给 System.Decimal 类型的成员,这是一个不可为空的值类型..”异常
    【解决方案5】:

    您可以使用临时查询进行初始查询吗?

    例如:

    var temp = (from t in _table
                where t.Id == id
                    && t.Time >= intervalStartTime
                    && t.Time <= intervalEndTime
                select t.Value) ??  new List<int>() {0};
    var value = temp.Average();
    

    不确定这是否有帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多