Protector one Protector one - 3 months ago 12
C# Question

LINQ Any failing?

Is there any reason why the

Any
function in the following code could fail?

var orders = db.Order.Where(order => order.Item.Any());
foreach (var order in orders)
{
var first = order.Item.First(); // NullReferenceException thrown here: order.Item == null
}


Maybe I'm simply misunderstanding something here, but it seems to me
order.Item
should never be null due to the earlier
Any
statement.

Edit: if I make sure the expression is evaluated and not deferred by putting a
ToList
there, I get another exception due to the
order.Item
sequence having no items, which also baffles me:

var orders = db.Order.Where(order => order.Item.Any()).ToList();
foreach (var order in orders)
{
var first = order.Item.First(); // System.InvalidOperationException thrown here: order.Item.Count == 0
}

Answer

The expression that computes orders is deferred, so when there are any nulls in db.Order's Item, you wouldn't find them until you are in the foreach loop.

You can see what's going on by adding a ToList() call:

var orders = db.Order.Where(order => order.Item.Any()).ToList();

Now the exception will be thrown before the loop.

You can fix this by adding an explicit null check in the Where condition, or by using the new ?. syntax of C# 6:

var orders = db.Order.Where(order => order.Item != null && order.Item.Any());

or

var orders = db.Order.Where(order => order?.Item.Any());

The order.Item.Count == 0 error may be of the same nature: since the check for Any() is done at the DB level, and the call for First() happens at some later time, chances are that the item that was there for Any() to succeed is gone by the time you call First().

You should be able to fix this by adding LoadWith option for Item.