Will
Will

Reputation: 421

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

Can someone explain why this exception is occuring the in the following LINQ query:

        return (from c in dc.Classifications
                where c.Id == classificationId
                select new Classification()
                {
                    Description = c.Description,
                    ParentId = Convert.ToInt16(c.ParentId),
                }).Single<Classification>();

dc is the datacontext, Classification is a class containing the int property ParentId. The ParentId column is a nullable int from a Sql Server database. In the case of the ParentId field being null in the database, the statement returns an InvalidOperationException.

In other words, why does the above query fail and 'int y = Convert.ToInt16(null);'
work?

Upvotes: 1

Views: 8243

Answers (3)

CodingWithSpike
CodingWithSpike

Reputation: 43698

If c.PartentId can be null, then Convert.ToInt16(null) would throw an exception.

Since you indicate that Classification.ParentId is an int, is there a reason you are using Convert.ToInt16 to make it a short instead? Wouldn't you want ToInt32 instead? For that matter, why convert at all? simply:

ParentId = c.ParentId ?? 0

...and just to nit-pick, technically you don't need to specify your type at the end of that Linq expression:

.Single<Classification>()

you can omit that, since it is determined by the compiler, and just do:

.Single()

Update:

Oh I see, I'm sorry I mis-read your original question. The question is really why does:

int y = Convert.ToInt16(null);

work, while the same thing in a Linq2Sql expression throws an exception.

I don't have a great answer to that, other than to point out that while the expressions look the same in code,t hey are actually handled by 2 differnet Linq implementations. (Much the same as an interface can have different backing implementations).

In the case of:

int y = Convert.ToInt16(null);

You are making a direct call to Convert.ToInt16. This seems to convert a null into default<T> where T is the desired type (so it return 0 in this case).

However when used in a Linq2Sql expression, the expression and its projection are handed off to Linq2Entities or Linq2Sql to process. There could just be a bug in that stuff somewhere. Used as a basic Linq2Objects (or whatever you want to call it) it actually seems to work OK:

[TestMethod] // test passes
public void TestLinqToObjects()
{
  var stuff = new List<int?>() { null };
  var y = (from x in stuff
           select Convert.ToInt32(x))
           .First();
  Assert.AreEqual(0, y);
}

The above "select" works, however putting the same Linq expression against a Linq2Sql or EntityFramework collection would cause a different implementation of the Linq processor to handle the expression,a nd it might do something different to try to optemise the expression, or to turn some of it into a SQL statement, or could just have bugs that the other implementations don't.

I know that doesn't really solve your issue, but it might help to explain it?

Upvotes: 1

Jon Hanna
Jon Hanna

Reputation: 113222

c.ParentId is of type int?

Your call to Convert.ToInt16 is then broken in two ways; it fails on null (as here) and it fails if ParentId is greater than short.MaxValue or smaller than short.MinValue.

Why is that call there? Remove it. Also, replace Single<Classification>() with SingleOrDefault() so that it can return null when appropriate.

Upvotes: 0

Daniel Hilgarth
Daniel Hilgarth

Reputation: 174289

Assuming that you want to have 0 as ParentId when it is NULL in the database:

    return (from c in dc.Classifications
            where c.Id == classificationId
            select new Classification()
            {
                Description = c.Description,
                ParentId = Convert.ToInt16(c.ParentId ?? 0),
            }).Single<Classification>();

My answer also assumes, that Classifications.ParentId is of type Nullable<int>/int? and its value is null if the column is NULL in the database.

The only thing I changed, was the ?? 0 part in the convert. It uses 0 in case c.ParentId is null and c.ParentId otherwise. See ?? Operator for more info.

Upvotes: 2

Related Questions