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

I got this query, however it fails if no votes are found with exception:

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

I assume its because sum returns an int and not a nullable int, giving sum a int? as input only give the same error, probably cause sum only workes on ints.

Any good workaround for this?

Rui Jarimba
  • 11,166
  • 11
  • 56
  • 86
AndreasN
  • 2,881
  • 3
  • 24
  • 29

14 Answers14

75

You want to use the nullable form of Sum, so try casting your value to a nullable:

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

Your issue is discussed here in more detail:

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

Scott Stafford
  • 43,764
  • 28
  • 129
  • 177
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() 
}

EDIT - ok what about this... (Shooting again since I don't know your model...):

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) 
}
Rashack
  • 4,667
  • 2
  • 26
  • 35
  • sorry wrong copy paste there. It won't compile because Sum() returns an int. And the ?? operator don't work on non-nullable types. – AndreasN Mar 30 '09 at 08:55
  • I am just shooting here - I assume that sum of empty collection should be 0. So the issue might be that v.Points is nullable? – Rashack Mar 30 '09 at 09:08
  • So v.Points is a sequence of some sort? – Dave Van den Eynde Mar 30 '09 at 09:21
  • This answer works, but it doesn't explain -why- yours didn't work. See http://weblogs.asp.net/zeeshanhirani/archive/2008/07/15/applying-aggregates-to-empty-collections-causes-exception-in-linq-to-sql.aspx or look at my answer below... – Scott Stafford Apr 19 '10 at 17:54
  • Could it be because SQL returns `DBNull` for `SUM()` over 0 rows? – binki May 09 '14 at 21:06
20

Assuming "v.Points" is a decimal just use the following:

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
}
Jeroen Bernsen
  • 201
  • 2
  • 2
  • The interesting thing about the **cast to "decimal?"** is that the generated T-SQL code has no difference with the code generated if you don't do the cast! I didn't try it before thinking that the generated T-SQL code would use CAST(...) – vcRobe Oct 24 '14 at 17:45
  • 1
    That's because that information is important to the .NET side as it injects the retrieved data into the objects. – argyle Feb 11 '15 at 22:12
15

Try to check this out:

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

So, the root of the problem is that SQL query like this:

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

returns NULL

Pavel Shkleinik
  • 6,298
  • 2
  • 24
  • 36
11

If you don't like casting to nullabe decimal you could also try using Linq To Objects with ToList() method,

LinqToObjects Sum of empty collection is 0, where LinqToSql Sum of empty collection is null.

Emir
  • 1,586
  • 3
  • 16
  • 32
5

A simple but effective workaround would be to only sum the votes where Points.Count > 0, so you never have null values:

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()
}
Razzie
  • 30,834
  • 11
  • 63
  • 78
4

Just to add another method into the mix :)

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

I had a similar scenario but I wasn't comparing an additional field when summing...

Where(q => q.FinalValue.HasValue).Sum(q=>q.FinalValue.Value);
nhahtdh
  • 55,989
  • 15
  • 126
  • 162
Phil
  • 1,609
  • 12
  • 24
3

Assuming Points is a List of Int32, how about something like:

var sum = Points.DefaultIfEmpty().Sum(c => (Int32)c ?? 0)
ProfNimrod
  • 4,142
  • 2
  • 35
  • 54
2

I had the same problem. Solved it with empty list union:

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

In case of "Points" is integer this should work.

Alex
  • 21
  • 1
2

I think this is the same case. I resolved it. This is my solution:

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

I hope this help.

Alberto De Caro
  • 5,147
  • 9
  • 47
  • 73
Pepa
  • 31
  • 1
1

Thought I would throw another solution out there. I had a similar issue and this is how I ended up solving it:

Where(a => a.ItemId == b.ItemId && !b.IsPointsNull()).Sum(b => b.Points)
Jeff
  • 11
  • 1
0

I had a similar issue and came up with the solution of getting whatever I was trying to get out of the database, do a count on those and then only if I had anything returned do a sum. Wasn't able to get the cast working for some reason so posting this if anyone else had similar issues.

e.g.

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

And then check to see if you've got any results so that you don't get null returned.

If (Votes.Count > 0) Then
    Points = Votes.Sum(Function(v) v.Points)
End If
tcmorris
  • 334
  • 4
  • 6
0

Similar to previous answers, but you can also cast the result of the entire sum to the nullable type.

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
}

Arguably this better fits what is really going on but it has the same effect as the cast in this answer.

Community
  • 1
  • 1
OlduwanSteve
  • 1,263
  • 14
  • 16
0
        (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);

This works, but isn't very pretty or readable.

AndreasN
  • 2,881
  • 3
  • 24
  • 29
  • In this case, shouldn't Rashack's answer work for you? I take it you want to get all items with corresponding number of votes, and if the votecount in the database is null (DBNull), then set Points to 0. – Razzie Mar 30 '09 at 10:58