The World’s Leading Microsoft .NET Magazine
   
 
The .NET Addict's Blog

My Top Tags

                                                           

My RSS Feeds








Latest Diggs - Programming

Internet Blogs - Blog Top Sites

Site Hits

Total: 2,551,710
since: 19 Jan 2005

Unexpected and Unsafe behavior in LINQ

posted Wed 14 Nov 07

Imagine if you will that you have a variable of type List<Customer> and you are doing some querying on this variable. Everyone, no matter what application they're writing, will at some point need to write code that retrieves an element from a list. Contains and IndexOf do a pretty good job, but they only work in limited fashion. What if you need to retrieve the customer from the list that has a specific social security number, but your object is not using SSN in it's CompareTo implementation? In the "old days" without LINQ, you'd have to write code to troll through the list and pick up the target customer if you found a match on SSN. Today, you can write a LINQ query to obtain that customer.

The problem comes from the fact that LINQ will not return null when you use the First() method. Take a look at a sample query, one that would be perfectly acceptable to anybody who has a background in SQL statements:

var q = from Customer cust in custList where cust.SSN="555-55-5555" select cust;

This is great, but you're going to get an array, not a single customer. You know ahead of time that you're only going to get a single customer from thsi query, so you think - maybe I'll use the "First()" method like this:

Customer cust = (from Customer cust in custList where cust.SSN="555-55-5555" select cust).First();

At first glance this looks pretty good. But, what happens if there is no customer in that list with that SSN? Personally, I would like cust to be null. But what really happens is LINQ throws an InvalidOperation exception and completely ruins your code and your execution context. So, I figure, maybe some refactoring is in order:

Customer cust = custList.First( c => c.SSN = "555-55-5555" );

That's got to work, right? Nope. You still get an invalid operation when the filter function doesn't match. To get around this, I wrote a language extension called SafeFirst that wraps both forms of First<T>() in checks for a positive result count:

static class ThisShouldBeUnnecessary
{
public static T SafeFirst<T>(this IEnumerable<T> input, Func<T,bool> func)
{
    IEnumerable<T> results = input.Where(func);
    if (results.Count() > 0)
        return results.First();
    else
        return default(T);
}
public static T SafeFirst<T>(this IEnumerable<T> input)
{
    if (input.Count() > 0)
        return input.First();
    else
        return default(T);
}
}

And now just replace your First() method calls with SafeFirst and you'll get null as you would expect. In fact, you get the default value for the type "T" so that would be a 0 for an int, null for any pointer type, DateTime.MinValue for a date, etc.

This kind of workaround should not be necessary, and if anybody else has a more elegant way around this particular problem, I'd love to see it.

tags:    

links: digg this    del.icio.us    technorati    reddit




1. Kazi left...
Wed 14 Nov 07 4:41 pm

the builtin FirstOrDefault LINQ operator does exactly this


2. Kevin Hoffman left...
Wed 14 Nov 07 4:44 pm

This is exactly why reliance on Intellisense has made me such a lazy developer. I don't see that in my intellisense dropdown on an IEnumerable, but I do see First() ... I wonder why? I'll have to check it out and, if it works, that fixes my problem without me having to create a new language extension and re-invent the wheel. Thanks for the heads-up!


3. Daniel Phelps left...
Fri 02 May 08 8:35 pm :: http://www.danielphelps.net/

I'm pretty sure Single(), First() and the like throw exceptions because a couple things can go wrong in a LINQ query, so you should 'try' the whole shebang. The exception caught may or may not have something to do with Single() for First().

You could write a class simply for Data Access... if you're expecting a single item from a particular method in your class but it does not exist (Single() or First() throws an exception(), that method should catch the exception and return null.

for example: DataAccess db = new DataAccess();

Customer c = db.getCustomerBySSN(stringSSN); //now... is c null? or is it good to go?


Tag Related Posts

CLINQ v1.1.0.0 Released!

Fri 02 May 08 5:38 P GMT-05
tags:          

LINQ to REST - A much better name for Astoria

Tue 11 Dec 07 1:23 P GMT-05
tags:        

Unexpected and Unsafe behavior in LINQ

Wed 14 Nov 07 8:09 P GMT-05
tags:    

Continuous LINQ - Can I write games with it?

Mon 13 Aug 07 3:09 P GMT-05
tags:        

Continuous LINQ

Mon 06 Aug 07 1:21 P GMT-05
tags:    

Dynamic, Observable LINQ Views

Tue 31 Jul 07 1:21 A GMT-05
tags:        

Installing Orcas Beta 1 - VMware Style

Mon 23 Apr 07 12:16 P GMT-05

Visual Studio "Orcas" - March CTP is Available

Wed 28 Feb 07 12:28 P GMT-05
tags:            

Objective-C Categories vs C# 3.5 Language Extensions

Mon 26 Feb 07 1:05 P GMT-05
tags:                

DLinq vs the ADO.NET Entity Framework

Fri 23 Jun 06 4:01 P GMT-05

WPF/XAML and LINQ - A match made in heaven

Tue 06 Jun 06 11:32 A GMT-05
tags:                  

Language Extensions in C# 3.0

Wed 31 May 06 2:17 P GMT-05

Lambda Lambda Lambda

Sun 21 May 06 1:01 A GMT-05

The Adventures of LINQ (Not Zelda)

Fri 19 May 06 11:21 P GMT-05
tags: