总和可为空的 Linq 查询

Posted

技术标签:

【中文标题】总和可为空的 Linq 查询【英文标题】:Linq query with nullable sum 【发布时间】:2010-10-16 08:03:26 【问题描述】:
from i in Db.Items
select new VotedItem

    ItemId = i.ItemId,
    Points = (from v in Db.Votes
              where b.ItemId == v.ItemId
              select v.Points).Sum()

我收到了这个查询,但是如果没有发现异常投票,它就会失败:

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

我假设它是因为 sum 返回一个 int 而不是一个可为空的 int,所以给 sum 一个 int?由于输入只给出相同的错误,可能导致 sum 仅适用于整数。

有什么好的解决方法吗?

【问题讨论】:

AndreasN,我无法以任何方式重现您的问题...... v.Points 的类型是什么?是 int, int?, List .... 它的 linq2Sql,应该这么说。 仍然 - v.Points 的类型是什么?整数整数? IEnumerable 我觉得DefaultIfEmpty不能用来解决这些问题太可惜了。无论出于何种原因,当它在查询和 Sum 方法之间楔入时,它似乎不起作用。 【参考方案1】:

您想使用 Sum 的可为空形式,因此请尝试将您的值转换为可为空的:

from i in Db.Items
select new VotedItem

    ItemId = i.ItemId,
    Points = (from v in Db.Votes
              where b.ItemId == v.ItemId
              select v.Points).Sum(r => (decimal?) r.Points)

这里更详细地讨论了您的问题:

http://weblogs.asp.net/zeeshanhirani/archive/2008/07/15/applying-aggregates-to-empty-collections-causes-exception-in-linq-to-sql.aspx

【讨论】:

【参考方案2】:
from i in Db.Items
select new VotedItem

    ItemId = i.ItemId,
    Points = (from v in Db.Votes
              where b.ItemId == v.ItemId
              select v.Points ?? 0).Sum() 

编辑 - 好吧,这个怎么样...(再次拍摄,因为我不知道你的型号...):

from i in Db.Items
select new VotedItem

    ItemId = i.ItemId,
    Points = (from v in Db.Votes
              where b.ItemId == v.ItemId)
              .Sum(v => v.Points) 

【讨论】:

抱歉复制粘贴错误。它不会编译,因为 Sum() 返回一个 int。和??运算符不适用于不可为空的类型。 我只是在这里拍摄 - 我假设空集合的总和应该为 0。所以问题可能是 v.Points 可以为空? 所以 v.Points 是某种序列? 这个答案有效,但不能解释 - 为什么 - 你的答案无效。请参阅weblogs.asp.net/zeeshanhirani/archive/2008/07/15/… 或在下面查看我的答案... 会不会是因为SQL返回DBNull for SUM()超过0行?【参考方案3】:

假设“v.Points”是小数,只需使用以下内容:

from i in Db.Items
select new VotedItem

    ItemId = i.ItemId,
    Points = (from v in Db.Votes
              where b.ItemId == v.ItemId
              select (decimal?) v.Points).Sum() ?? 0

【讨论】:

强制转换为“十进制?”的有趣之处在于生成的T-SQL代码与不强制转换生成的代码没有区别!没想到生成的 T-SQL 代码会使用 CAST(...) 之前没有尝试过 这是因为该信息对 .NET 端很重要,因为它将检索到的数据注入到对象中。【参考方案4】:

尝试检查一下:

var count = db.Cart.Where(c => c.UserName == "Name").Sum(c => (int?)c.Count) ?? 0;

所以,问题的根源是这样的 SQL 查询:

SELECT SUM([Votes].[Value])
FROM [dbo].[Votes] AS [Votes]
WHERE 1 = [Votes].[UserId] 

返回 NULL

【讨论】:

【参考方案5】:

如果您不喜欢转换为 nullabe 十进制,您也可以尝试使用 Linq To Objects 和 ToList() 方法,

LinqToObjects 空集合之和为0,其中LinqToSql 空集合之和为null。

【讨论】:

【参考方案6】:

一个简单但有效的解决方法是只对 Points.Count > 0 的投票求和,因此您永远不会有空值:

from i in Db.Items
select new VotedItem
    
  ItemId = i.ItemId,
  Points = (from v in Db.Votes
            where b.ItemId == v.ItemId &&
            v.Points.Count > 0
            select v.Points).Sum()

【讨论】:

【参考方案7】:

只是为了添加另一种方法:)

Where(q=> q.ItemId == b.ItemId && b.Points.HasValue).Sum(q=>q.Points.Value)

我有类似的情况,但在求和时我没有比较其他字段...

Where(q => q.FinalValue.HasValue).Sum(q=>q.FinalValue.Value);

【讨论】:

【参考方案8】:

假设 Points 是一个 Int32 的列表,那么类似:

var sum = Points.DefaultIfEmpty().Sum(c => (Int32)c ?? 0)

【讨论】:

【参考方案9】:

我遇到了同样的问题。用空列表联合解决它:

List<int> emptyPoints = new List<int>()  0 ;

from i in Db.Items
select new VotedItem

 ItemId = i.ItemId,
 Points = (from v in Db.Votes
           where b.ItemId == v.ItemId
           select v.Points).Union(emptyPoints).Sum()

如果“点”是整数,这应该可以工作。

【讨论】:

【参考方案10】:

我认为情况相同。 我解决了。这是我的解决方案:

var x = (from a in this.db.Pohybs
                 let sum = (from p in a.Pohybs
                            where p.PohybTyp.Vydej == true
                            select p.PocetJednotek).Sum()
                 where a.IDDil == IDDil && a.PohybTyp.Vydej == false
                 && ( ((int?)sum??0) < a.PocetJednotek)
                 select a);

希望对你有所帮助。

【讨论】:

【参考方案11】:

我想我会抛出另一个解决方案。我有一个类似的问题,这就是我最终解决它的方式:

Where(a => a.ItemId == b.ItemId && !b.IsPointsNull()).Sum(b => b.Points)

【讨论】:

【参考方案12】:
        (from i in Db.Items
         where (from v in Db.Votes
                where i.ItemId == v.ItemId
                select v.Points).Count() > 0
         select new VotedItem
         
             ItemId = i.ItemId,
             Points = (from v in Db.Items
                       where i.ItemId == v.ItemId
                       select v.Points).Sum()
         ).Union(from i in Db.Items
                  where (from v in Db.Votes
                         where i.ItemId == v.ItemId
                         select v.Points).Count() == 0
                  select new VotedItem
                  
                      ItemId = i.ItemId,
                      Points = 0
                  ).OrderBy(i => i.Points);

这可行,但不是很漂亮或可读性。

【讨论】:

在这种情况下,Rashack 的答案不应该对您有用吗?我认为你想获得所有具有相应票数的项目,如果数据库中的票数为空(DBNull),则将 Points 设置为 0。【参考方案13】:

我遇到了类似的问题,并想出了一个解决方案,即从数据库中取出我试图从数据库中取出的任何东西,对它们进行计数,然后只有当我返回任何东西时才进行总和。由于某种原因无法让演员正常工作,因此如果其他人有类似问题,请发布此内容。

例如

Votes = (from v in Db.Votes
          where b.ItemId = v.ItemId
          select v)

然后检查是否有任何结果,以免返回 null。

If (Votes.Count > 0) Then
    Points = Votes.Sum(Function(v) v.Points)
End If

【讨论】:

【参考方案14】:

与之前的答案类似,但您也可以将整个总和的结果转换为可空类型。

from i in Db.Items
select new VotedItem

    ItemId = i.ItemId,
    Points = (decimal?)((from v in Db.Votes
              where b.ItemId == v.ItemId
              select v.Points).Sum()) ?? 0

可以说这更符合实际情况,但它与this answer 中的演员阵容具有相同的效果。

【讨论】:

以上是关于总和可为空的 Linq 查询的主要内容,如果未能解决你的问题,请参考以下文章

中继/ graphql:可为空的响应或捕获查询错误的方法

将列更改为可为空的查询不起作用[重复]

将可为空的 int 映射到可为空的 int + automapper

检查可为空的布尔值是不是为空[重复]

如果它为空或为空,如何从序列化中忽略可为空的属性?

为啥 TargetNullValue 更新可为空的 Source