Tuesday, February 23, 2010

Expressions, functions, and case sensitivity

I've been developing a LINQ library with some useful classes and extensions to support, primarily, testability with my LINQ-based data layers. To this end, I have an IDataContextWrapper interface and a DataContextWrapper<T> generic implementation (where T derives from DataContext). I notice in my use of this code I was making calls looking like:

var user = this.ContextWrapper
.Table<User>()
.SingleOrDefault( u => u.Username == username );

Here, Table<T>() is the method on the wrapper that returns the strongly typed Table corresponding to the type T from the wrapped DataContext.

I thought to myself, this would be a lot shorter if I simply added a method to my wrapper class to get the single (or first) matching element from the table. So I proceeded, in my naive way, to add such a method.

public T SingleOrDefault<T>( Func<T,bool> selector )
{
return this.db.GetTable<T>().SingleOrDefault( selector );
}

Then simplify my code from above, slightly, to:

var user = this.ContextWrapper
.SingleOrDefault<User>( u => u.Username == username );

I did the same for Single(), FirstOrDefault(), and First().

After awhile I started getting reports of spurious errors in some code that is dependent on this library. Perhaps, you can see the subtle error lurking in the above. Here'a hint: the failures that were being reported all had to do with usernames that had uppercase characters in the database, but were typed in as lowercase in the login box.


It turns out that the extension method SingleOrDefault() that takes a Func<T,bool> performs the function, not by translation to SQL (duh), but by loading up the table and applying the function to each result in turn to filter the matches. What I really needed to use was the signature that takes an Expression<Func<T,bool>>. This allows the expression to be translated to SQL and performs the query in the same a case insensitive way as the original query.


public T SingleOrDefault<T>( Expression<Func<T,bool>> selector )
{
return this.db.GetTable<T>().SingleOrDefault( selector );
}

The most interesting bit is that I didn't need to change my calling code at all, as the lambda expression maps equally well onto the Func<T,bool> or the Expression<Func<T,bool>>.

Just another subtle thing to remember when using or extending the LINQ classes.

1 comment :

Comments are moderated.