【问题标题】:Sum() causes exception instead of returning 0 when no rowsSum() 在没有行时导致异常而不是返回 0
【发布时间】:2010-10-09 06:50:01
【问题描述】:

我有这个代码(好吧,我没有,但是类似的东西:p)

    var dogs = Dogs.Select(ø => new Row
    {
            Name = ø.Name,
            WeightOfNiceCats = ø.Owner
              .Cats
              .Where(æ => !æ.Annoying)
              .Sum(æ => æ.Weight),
    });

在这里,我遍历所有狗并总结所有与狗拥有相同主人的不烦人的猫的重量(成一个不可为空的小数)。当然,几乎所有的猫都很烦人,所以我得到了这个错误:

null 值不能分配给 System.Decimal 类型的成员,该类型是不可为 null 的值类型。

所使用的字段或外键都不能为空。因此,当Where 子句不返回任何猫时,就会发生错误,这通常会发生。但是我该如何解决呢?我希望它在发生这种情况时返回 0。在Where 子句之后尝试使用DefaultIfEmpty(),但随后出现此错误:

对象引用未设置为对象的实例。

我想这是可以理解的。我尝试在Sum 之后添加??,但由于此错误而无法编译:

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

这当然也有道理。那么我能做什么呢?如果 Sum 在没有什么可总结的情况下只是返回 0 那就太好了。或某种SumOrZero 声明。制作与 Linq2SQL 一起使用的SumOrZero 方法会不会很困难?

【问题讨论】:

  • 好问题。很遗憾,这种行为似乎没有记录在 MSDN 中。

标签: c# linq-to-sql nullreferenceexception


【解决方案1】:

你已经给出的技巧 (.Sum(æ => (decimal?) æ.Weight) ?? 0.0M) 是我能想到的最好的技巧,当它在数据库中作为查询执行时。有点烦人,但还有更糟糕的事情......

与 LINQ-to-Objects 不同,您不能只添加自己的扩展方法,因为它需要由底层提供程序映射。

【讨论】:

  • 完全正确。这就是我害怕的,呵呵。
【解决方案2】:

在 LINQ to Objects 中这很容易。使用 LINQ to SQL 会更难。您可以编写自己的扩展方法,该方法调用Queryable.Sum,并使用从正常表达式构建的表达式,并强制转换为可空类型。我怀疑你需要这样做?? 0m 虽然在调用代码中。换句话说,你可以这样做:

.SumOrNull(æ => æ.Weight) ?? 0M,

.SumOrNull 的签名在哪里:

public static decimal? SumOrNull<TSource, decimal>(this IQueryable<TSource>,
    Func<TSource,decimal> projection)

您基本上可以为 Queryable 中支持的所有值类型编写它。您可以通用地编写它,并使用反射在 Queryable 中调用适当的方法,但这也很麻烦。

老实说,我认为你是最好的。

【讨论】:

    【解决方案3】:

    您想使用 DefaultIfEmpty,它将返回一个带有单个元素 0 的 IEnumberable,这在语义上与您的要求相同。

    由于您有一个可以为空的小数,您可能必须使用采用 2 个参数的方法的第二个版本。

    【讨论】:

    • 恰恰相反;小数点不可为空。 LINQ-to-SQL 故障的解决方法 是使其可以为空以进行计算。
    • DefaultIfEmpty 导致“对象引用未设置为对象的实例。”
    • 搞清楚你的​​逻辑哪里错了。或者更好的是,发布损坏的代码,这样我们就可以看到你哪里出错了。 NullReferenceException 99% 的情况都是你的错。
    • 嗯,你在说什么?我有?代码和不同的错误消息。甚至你所说的那个。你读过我的问题吗?
    • 你用错了......你需要将一个非空枚举传递给 DefaultIfEmpty,你没有这样做(因此出现空异常)。没那么难吧?
    【解决方案4】:

    这就是我现在的结果:

    .Sum(æ => (decimal?) æ.Weight) ?? 0.0M,
    

    这就像一个魅力。

    我仍然希望有一个我可以使用的SumOrZero,它的工作方式与普通的Sum 完全相同,只是它永远不会因任何原因返回null。就像其他人指出的那样,这对于IEnumerable 来说很容易创建,但对于IQueryable 来说创建起来有点麻烦,所以它可以与Linq2SQL 等一起使用。所以我现在就把它留在那里。虽然如果有人有一天感到无聊并为IQueryable 写了一个SumOrZero,它适用于所有数字类型的Linq2SQL,请告诉我:D

    【讨论】:

    • 小错字——应该有decimal*
    • @Daniel 没错!而且只用了 5 年就有人注意到并通知了:p
    猜你喜欢
    • 1970-01-01
    • 2021-12-15
    • 1970-01-01
    • 1970-01-01
    • 2018-06-07
    • 1970-01-01
    • 1970-01-01
    • 2018-11-19
    • 1970-01-01
    相关资源
    最近更新 更多