Friday, August 1, 2014

How To Make Good Use of C# Expressions

If you use Entity Framework, or really anything that returns an IQueryable, and you want to perform LINQ operations on that Enumerable, you’ve probably discovered several things that just don’t work.
This is mostly due to the fact that with Entity Framework, your LINQ statements must be translatable into SQL. Therefore, you’re pretty much limited to using Expressions in your LINQ methods.
I make use of this technology regularly, and I will admit, at first, the subtle paradigm shift threw me for a loop. While I’m not out to demystify IQueryables, I am out to share a “better practice” I discovered while using Entity Framework to help provide consistent filtering to your collections.
Say you have a collection of objects with the following definition:

public class Cat {
    public int ID { get; set; }
    public string Name { get; set; }
    public bool IsAlive { get; set; }
    //...
}
You’ll probably find yourself writing the following code more than once:

cats.Where(c => c.IsAlive);
Or possibly:

cats.Where(c => c.Name.ToLower().Contains(query.ToLower());
(for some string query)
If you’re like me, then you probably don’t want to write this any more than you need to. Since you’re using IQueryables, you have a few options, really. You could write extensions to your IQueryables, or you could do something cooler. That’s where Expressions come in.
For example, say you want a reusable query to search for living cats. This is fairly straightforward, but making a reusable query is not too much more difficult.

public static Expression<Func<Cat, bool>> CatsAreAlive = (c) =>
    c.IsAlive
    ;
//…
var livingCats = cats.Where(CatsAreAlive);
This is great if you ever anticipate your criteria for living cats to change. Such as if you added a DateTime? DateOfDeath to the cat object. Then you would only have to change your CatsAreAlive expression, and everywhere that called it would be updated:

public static Expression<Func<Cat, bool>> CatsAreAlive = (c) =>
    c.IsAlive
    && c.DateOfDeath != null
    ;
//…
var livingCats = cats.Where(CatsAreAlive); // no zombies plz
Or say you want a reusable query to search cats by name. As you can see from the LINQ above (third from top), this is a little cumbersome to have to write each time you want to search for cats.
Using an expression, you can do the following:

public static Expression<Func<Cat, bool>> CatNameLike(string query) {
    return (c) =>
        c.Name.ToLower().Contains(query.ToLower())
    ;
}
//…
var queryCats = cats.Where(CatNameLike(query));
Then if you add a string Nickname to the cat, you only have to change it once.

public static Expression<Func<Cat, bool>> CatNameLike(string query) {
    return (c) =>
        c.Name.ToLower().Contains(query.ToLower())
        || c.Nickname.ToLower().Contains(query.ToLower())
    ;
}
//…
var queryCats = cats.Where(CatNameLike(query));
Awesome.
You can do this with any LINQ expression, such as Selects:

public static Expression<Func<Cat, SelectListItem>> CatsForDropdownList = (c) =>
    new SelectListItem() {
        value = c.ID.ToString(),
        label = c.Name,
    }
    ;
//…
var dropdown = cats.Select(CatsForDropdownList);
OrderBys:

public static Expression<Func<Cat, string>> NameOrder = (c) =>
    c.Name.ToLower().StartsWith("the ") ? c.Name.Substring(4) :
                                          c.Name
    ;
//…
var herdedCats = cats.OrderBy(NameOrder);
And any other IQueryable LINQ extension. The important point thing to note is that LINQ expressions that don’t require an argument should be defined as Parameters, while ones that do should be defined as Methods. This isn’t required, they all could be Methods. It’s just prettier (IMHO) to not see all those empty parentheses.
One additional cool thing you can do once you’ve defined these Expressions is to make use of them as Functions in other contexts. For example, say you want to check a single Cat object for a matching name (I can’t provide a good example, but I’m sure it could happen). You could do the following:

public static bool NameLike(this Cat cat, string query) {
    return CatNameLike(query).Compile()(cat);
}
(I couldn’t resist an extension method)
While this is a roundabout way of running this query, it gives you a definite advantage: you only had to write the name matching logic once, and when you decide to change it (say you add a FirstName and a LastName too), you only need to change it once, and it will be available everywhere.
I recommend implementing these static methods and parameters in a static class, so you can keep them all together. Or putting them in classes by type (e.g., Filters, Sorts, Selects, etc.).
I must end by saying that while I prefer this method for its apparent elegance, I’m always open to suggestions on how to do it more elegantly. I’d be glad to hear your opinion on the matter.

No comments: