Protector one
Protector one

Reputation: 7261

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
}

Upvotes: 2

Views: 566

Answers (3)

Harald Coppoolse
Harald Coppoolse

Reputation: 30464

It is simpler than you think. According to the definition of Enumerable.Any the ArgumentNullException is thrown if source is null.

Your class Order has a property Order.Item. One of your orders has an Item with value null. Your code should be:

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

Upvotes: 0

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726579

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.

Upvotes: 8

ChrisF
ChrisF

Reputation: 137148

This line:

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

won't actually evaluate until it needs to, so appears to fail later on.

If you replace the line with:

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

you'll see the failure immediately as the query is evaluated when the list is generated.

Replace the line with:

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

and it should work OK

Upvotes: 4

Related Questions