210

I have the following code. I'm getting error:

"The cast to value type 'Int32' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type."

when CreditHistory table has no records.

var creditsSum = (from u in context.User
                  join ch in context.CreditHistory on u.ID equals ch.UserID                                        
                  where u.ID == userID
                  select ch.Amount).Sum();

How can I modify the query to accept null values?

crthompson
  • 15,653
  • 6
  • 58
  • 80
zosim
  • 2,959
  • 6
  • 31
  • 34

8 Answers8

361

A linq-to-sql query isn't executed as code, but rather translated into SQL. Sometimes this is a "leaky abstraction" that yields unexpected behaviour.

One such case is null handling, where there can be unexpected nulls in different places. ...DefaultIfEmpty(0).Sum(0) can help in this (quite simple) case, where there might be no elements and sql's SUM returns null whereas c# expect 0.

A more general approach is to use ?? which will be translated to COALESCE whenever there is a risk that the generated SQL returns an unexpected null:

var creditsSum = (from u in context.User
              join ch in context.CreditHistory on u.ID equals ch.UserID                                        
              where u.ID == userID
              select (int?)ch.Amount).Sum() ?? 0;

This first casts to int? to tell the C# compiler that this expression can indeed return null, even though Sum() returns an int. Then we use the normal ?? operator to handle the null case.

Based on this answer, I wrote a blog post with details for both LINQ to SQL and LINQ to Entities.

Anders Abel
  • 67,989
  • 17
  • 150
  • 217
  • 3
    thanks Anders, the solution with DefaultIfEmpty(0).Sum() works fine for me. I have also tried the second solution with (int?) ... ?? 0..., but it throws the same exception as before.. – zosim Jul 28 '11 at 19:40
  • Finally got around to test this and adjusted it, so now the second version works too. – Anders Abel Jun 11 '12 at 21:07
  • 1
    Sum() and other aggregate functions will return null when applied to an empty dataset. Contrary to their definition, in reality they return a nullable version of the underlying type. – Suncat2000 Jul 18 '14 at 12:19
  • 2
    @recursive: Your example is LINQ-to-Objects, not LINQ-to-SQL (or LINQ-to-Entities). Their underlying data providers make them behave differently. – Suncat2000 Jul 07 '15 at 16:16
  • This was a good idea. I updated my return object to have nullable properties and that worked as a charm. – Kremena Lalova Aug 04 '15 at 19:30
8

To allow a nullable Amount field, just use the null coalescing operator to convert nulls to 0.

var creditsSum = (from u in context.User
              join ch in context.CreditHistory on u.ID equals ch.UserID                                        
              where u.ID == userID
              select ch.Amount ?? 0).Sum();
recursive
  • 83,943
  • 34
  • 151
  • 241
  • 1
    when I use your tip, compiler says: Operator '??' cannot be applied to operands of type 'int' and 'int'. do I forgot something? – zosim Jul 28 '11 at 19:31
  • @zosim: That is the reason to add the cast to `int?` first. – Anders Abel Jul 28 '11 at 19:44
  • i have added int?, but the same exception. I will grateful to you, when you will have dev env. to check what is wrong in this syntax. – zosim Jul 28 '11 at 19:54
  • 1
    @zosim: I don't understand the problem. If `Amount` is an `int`, then we are already sure it can not be null, and the coalescing is unnecessary. If you are getting the error you said, then `Amount` is not nullable, it's just an `int`, in which case perhaps you need to change your linq2sql dbml column in the designer to allow nulls. – recursive Jul 28 '11 at 20:14
  • 1
    @recursive: Amount is int, it's OK. Amount has already value. I think, that the error above is occured because CreditHistory table is empty. I have one record in User table and 0 records in CreditHistory table and error is occured. When I use DefaultIfEmpty(0).Sum() it works fine, but with ?? 0 it throws error. My another question is what is best practise in this case? DefaultIfEmpty(0) ? thanks – zosim Jul 28 '11 at 20:42
  • @zosim: Yes, that's exactly what DefaultIfEmpty is for. – recursive Jul 28 '11 at 22:44
6

I have used this code and it responds correctly, only the output value is nullable.

var packesCount = await botContext.Sales.Where(s => s.CustomerId == cust.CustomerId && s.Validated)
                                .SumAsync(s => (int?)s.PackesCount);
                            if(packesCount != null)
                            {
                                // your code
                            }
                            else
                            {
                                // your code
                            }
live-love
  • 48,840
  • 22
  • 240
  • 204
MohammadSoori
  • 2,120
  • 1
  • 15
  • 17
6

Had this error message when I was trying to select from a view.

The problem was the view recently had gained some new null rows (in SubscriberId column), and it had not been updated in EDMX (EF database first).

The column had to be Nullable type for it to work.

var dealer = Context.Dealers.Where(x => x.dealerCode == dealerCode).FirstOrDefault();

Before view refresh:

public int SubscriberId { get; set; }

After view refresh:

public Nullable<int> SubscriberId { get; set; }

Deleting and adding the view back in EDMX worked.

Hope it helps someone.

live-love
  • 48,840
  • 22
  • 240
  • 204
5

You are using aggregate function which not getting the items to perform action , you must verify linq query is giving some result as below:

var maxOrderLevel =sdv.Any()? sdv.Max(s => s.nOrderLevel):0
Prasad Jadhav
  • 5,090
  • 16
  • 62
  • 80
Ashwini
  • 91
  • 1
  • 1
  • 11
    This would make sdv execute twice. Which is not what you want for IQueryables – Ody Jul 20 '15 at 13:06
3

I see that this question is already answered. But if you want it to be split into two statements, following may be considered.

var credits = from u in context.User
              join ch in context.CreditHistory 
                  on u.ID equals ch.UserID                                        
              where u.ID == userID
              select ch;

var creditSum= credits.Sum(x => (int?)x.Amount) ?? 0;
LCJ
  • 22,196
  • 67
  • 260
  • 418
0

Got this error in Entity Framework 6 with this code at runtime:

var fileEventsSum = db.ImportInformations.Sum(x => x.FileEvents)

Update from LeandroSoares:

Use this for single execution:

var fileEventsSum = db.ImportInformations.Sum(x => (int?)x.FileEvents) ?? 0

Original:

Changed to this and then it worked:

var fileEventsSum = db.ImportInformations.Any() ? db.ImportInformations.Sum(x => x.FileEvents) : 0;
Ogglas
  • 62,132
  • 37
  • 328
  • 418
0

I was also facing the same problem and solved through making column as nullable using "?" operator.

Sequnce = db.mstquestionbanks.Where(x => x.IsDeleted == false && x.OrignalFormID == OriginalFormIDint).Select(x=><b>(int?)x.Sequence</b>).Max().ToString();

Sometimes null is returned.

Umang Raghuvanshi
  • 1,228
  • 15
  • 34