How to implement ISNULL(SUM(ColumnName),0) in Linq2SQL
I had a Linq2Sql statement like this:
var q = from o in db.OrderItem
where o.UserId == UserId
select o.Amount;
And then I was trying to get the sum of the OrderItem.Amount column by doing this:
total = q.Sum();
This worked fine as long as there were rows for this UserId in the OrderItem table. But if there are no rows in the table for this UserId I was getting this exception:
System.InvalidOperationException: The null value cannot be assigned to a member with type System.Decimal which is a non-nullable value type.
Linq2Sql was creating the following SQL query:
SELECT SUM([t0].[Amount]) AS [value]
FROM [dbo].[OrderItem] AS [t0]
WHERE [t0].[UserId] = @p0
which is fine but I really want this:
SELECT ISNULL(SUM([t0].[Amount]),0) AS [value]
FROM [dbo].[OrderItem] AS [t0]
WHERE [t0].[UserId] = @p0
Notice the ISNULL in the statement above that will return 0 if there are no rows found for this UserId.
Eventually I was able to track it down to var q being an IQueryable in the Linq2Sql statement above. By using ToArray() like this:
var q = (from o in db.OrderItem
where o.UserId == UserId
select o.Amount).ToArray();
total = q.Sum();
var q is now an IEnumerable (because ToArray() returns an IEnumerable<Decimal>). And instead of IQueryable.Sum() trying to apply an expression to the Null value, IEnumerable.Sum() will just loop through all of the items in the array and sum the values. In this case there are no items in the array so 0 is returned.
I could have also explicitly declared q as an IEnumerable<Decimal>:
IEnumerable<Decimal> q = from o in db.OrderItem
where o.UserId == UserId
select o.Amount;
total = q.Sum();
Just to note, the SQL statement is still not using ISNULL() but I am getting the result I want from within C#.
UPDATE (2010-07-22): Another solution from RichardD:
var q = from o in db.OrderItem
where o.UserId == UserId
select o.Amount;
Decimal total = q.Sum(a => (Decimal?)a) ?? 0M;
OR
IQueryable<Decimal?> q = from o in db.OrderItem
where o.UserId == UserId
select (Decimal?)o.Amount;
Decimal total = q.Sum() ?? 0;