具有可空求和的Linq查询

74
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?作为输入传递给sum仍会得到相同的错误,可能是因为sum只适用于整数。

这个问题有好的解决方法吗?


AndreasN,我无法以任何方式重现您的问题... v.Points 的类型是什么?它是 int、int?、List...? - bruno conde
它是Linq2Sql,应该说过了。 - AndreasN
仍然 - v.Points的类型是什么? int int? IEnumerable - Rashack
我认为很遗憾DefaultIfEmpty不能用来解决这些问题。不知何故,当将其插入查询和Sum方法之间时,它似乎无法正常工作。 - jpierson
14个回答

75

22
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类型。而且??运算符不适用于非可空类型。 - AndreasN
我只是在这里瞎猜 - 我认为空集合的总和应该是0。所以问题可能是v.Points可为空? - Rashack
那么v.Points是某种类型的序列吗? - Dave Van den Eynde
这个答案是正确的,但它没有解释为什么你的方法不起作用。请看http://weblogs.asp.net/zeeshanhirani/archive/2008/07/15/applying-aggregates-to-empty-collections-causes-exception-in-linq-to-sql.aspx或查看我的下面的答案... - Scott Stafford
是因为 SQL 在对 0 行进行 SUM() 操作时返回 DBNull 吗? - binki

20
假设 "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
}

关于将类型转换为 “decimal?” 的有趣之处在于,生成的 T-SQL 代码与您不进行转换时生成的代码没有任何区别!我以前没有尝试过这个,因为我认为生成的 T-SQL 代码会使用 CAST(...)。 - vcRobe
1
那是因为这些信息对于.NET端非常重要,因为它将检索到的数据注入到对象中。 - argyle

15

试着看看这个:

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


11
如果你不喜欢将值转换为可空(decimal?),你也可以尝试使用 Linq To Objects 的 ToList() 方法, LinqToObjects 中的空集合求和结果是0,而 LinqToSql 中的空集合求和结果是null。

5
一个简单但有效的解决方法是只对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()
}

4
只是想再增加一种方法 :)
Where(q=> q.ItemId == b.ItemId && b.Points.HasValue).Sum(q=>q.Points.Value)

我有一个类似的情况,但是在求和时没有比较额外的字段...

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

3
假设Points是一个Int32列表,那么可以这样写:
var sum = Points.DefaultIfEmpty().Sum(c => (Int32)c ?? 0)

2

我也遇到了同样的问题。通过使用空列表并集解决了这个问题:

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()
}

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

2

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

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);

我希望您能从中受益。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接