chum of chance
chum of chance

Reputation: 6300

Why is this if/then statement returning as true?

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?

Upvotes: 2

Views: 303

Answers (8)

John K
John K

Reputation: 28897

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.

Upvotes: 0

Eric Lippert
Eric Lippert

Reputation: 660279

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.

Upvotes: 17

Ben Voigt
Ben Voigt

Reputation: 283733

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;
}

Upvotes: 1

svick
svick

Reputation: 244908

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()).

Upvotes: 0

Steve Townsend
Steve Townsend

Reputation: 54168

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.

Upvotes: 0

Tim Jarvis
Tim Jarvis

Reputation: 18815

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())
{
  .....
}

Upvotes: 2

Yuliy
Yuliy

Reputation: 17728

A sequence with no elements is still an object.

Upvotes: 0

Andrew Barber
Andrew Barber

Reputation: 40150

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);
  }
}
}

Upvotes: 7

Related Questions