2

I have a rather ugly service job that runs through a legacy database and compares it to our production database:

if (vendorContract.Item.Where(x => x.ItemNumber == contractItem.Item_Number) != null) { var oldDbContractItem = vendorContract.Item.Where(x => x.ItemNumber == contractItem.Item_Number).First(); // check to see if there were changes if (oldDbContractItem.DateStamp != vendorContractItem.Date_Stamp) { oldDbContractItem.Update(vendorContractItem); } } 

I will get an error on var oldDbContratItem, "Sequence contains no elements", yet I just did a != null check. This must be simple, what's going on?

8 Answers 8

17

If I could teach people just one thing about LINQ it's this: the value of a query expression is an object that represents the query, not the results of the query. Fundamentally that's your problem; you're treating the query as its results. A query isn't a result any more than a restaurant is a club sandwich. A restaurant is a device which produces club sandwiches; a query is a device that produces results.

Sign up to request clarification or add additional context in comments.

1 Comment

I like this answer more than mine, from a fundamentals POV.
7

This is the test against null you are doing:

vendorContract.Item.Where(x => x.ItemNumber == contractItem.Item_Number) != null 

That will always be true; That will always return at least an empty sequence... never a null.

You might be meaning to test that its length is greater than 0?

There's an easier way, though, IMO. Call FirstOrDefault() instead of First() and leave out the pre-test completely. Then instead, test if the result of FirstOrDefault() is null.

var oldDbContractItem = vendorContract.Item .Where(x => x.ItemNumber == contractItem.Item_Number).FirstOrDefault(); if(oldDbContractItem != null) //would be null if there are no items { // check to see if there were changes if (oldDbContractItem.DateStamp != vendorContractItem.Date_Stamp) { oldDbContractItem.Update(vendorContractItem); } } } 

Comments

2

Because your query returned a container, it just happened to be empty, the null check is on the return not what the return contains.

try this instead...

if (vendorContract.Item.Where(x => x.ItemNumber == contractItem.Item_Number).Any()) { ..... } 

3 Comments

Note, in your specific case, @Andrew Barber has the more elegant solution (as you are wanting the first element if it exists anyway), my solution is for just testing the sequence contains something.
+1 This would be the way to go when, for instance, you would be dealing with (potentially) multiple items in a result sequence, instead of just one.
@Andrew: foreach would be the way to go if you are dealing with potentially multiple items. Running the query a second time just to call Any is silly (or worse, if the query is against a shared data store, it introduces a race condition).
1

Don't run the query twice. It's inefficient and may introduce a race condition into your code. Also, your logic is much better supported by either using IEnumerator<T> directly, or with a foreach loop.

Use either:

var result = vendorContract.Item.Where(x => x.ItemNumber == contractItem.Item_Number).GetEnumerator(); if (result.MoveNext) { var oldDbContractItem = result.Current; // ... } 

or

foreach (var oldDbContractItem in vendorContract.Item.Where(x => x.ItemNumber == contractItem.Item_Number)) { // ... break; } 

1 Comment

+1 for noting that the original code was needlessly running the query twice.
0

A sequence with no elements is still an object.

Comments

0

Where can return a non-null value, but still resolve to a sequence containing no elements. In your failing statement you are using something else, which is Where().First(). That will be null if the sequence from Where is indeed empty.

Comments

0

There is difference between null and an empty sequence (e.g. something similar to new List<T>()). What you want to do is to use Any() to check whether the sequence contains any elements, or rather replace First() with FirstOrDefault(), which returns null if the sequence contains no element. (If you use that, make sure that null is not a valid value that could be returned by First()).

Comments

0

After using a fix from the accepted answer, you can further debug the LINQ situation (if need be) by using the following steps. LINQ is an exciting technology but takes a while to wraps ones' head around it - a bit of a paradigm shift I would say. Eric's answer hit the nail on the head because he likely helped build the stuff!

Debugging Steps for LINQ

  1. To deal with the issue of no results from the second statement change .First() to .FirstOrDefault() If nothing is found it will return the default value of the data type - if the data type is a class it will return a null value. Your second statement should then work with a null check too and without error.

  2. Then you can debug your LINQ statement to find out why it's doing what it does.

    • if using LINQ to SQL, Intellisense in Visual Studio 2010 will show you the SQL generated when you hover over a query variable (not the result of a query). If you need the visualizer for VS 2008 it's here

    • Similarily if you're using LINQ to Entity Framework, you can get the generated SQL using the visualizer plugin here.

  3. I always take the generated SQL from these tools, paste it directly into a query window and run it there. It will show you the empty set you're getting back and if that's a problem you can further debug it in this manner of visualizing the generated statements.

3 Comments

This is not actually solving anything. The problem is that his test for an empty sequence is wrong. You're just masking the bug.
@Yuliy: the accepted answer also suggested FirstOrDefault as a step in debugging. It's not masking anything but rather making the process more manageable.
....but you're right if you noticed I was focusing on the second code statement more than the first, not about the truth test. I will leave these tool hyper-links here in case they prove useful to somebody debugging a LINQ situation.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.