Reputation: 421
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
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
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
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