Composable and Efficient LINQ Queries

Whether you come from a functional, procedural, or object oriented background, it’s always a good idea to promote code reuse as much as possible. One of the best ways to have reusable LINQ queries is to write them in a composable fashion.

Put simply, composable methods are those that you can chain together to build the desired functionality out of smaller parts. Take the following for example:

regularquery

Although its a bit contrived, if you wanted a slightly different result set, you would have to recreate most of the query logic. If, however, you used composable methods like the following you only have to create the new logic that’s required instead of reinventing the wheel:

composedqueries1

Creating composable methods is actually quite easy, essentially all you need to do is make sure the return type of a method is the same as the input type. For instance, the following could be the signature for the FilterByType method:

filterbytype

As any good little programmer should do, most of us use the most basic type possible for our method signatures. The problem is, that could be the exact opposite of what should be done. Although IQueryable<T> inheirets from IEnumerable<T>, it is often the case that using IQueryable<T> is more appropriate.

Why is this an issue? Well, the LINQ queries are evaluated differently on IQueryables than on IEnumerables. More specifically, queries on IEnumerable collections are evaluated immediately while those on IQuerable collections aren’t evaluated until you enumerate over the results (which includes converting the collection to an array, list, or any other type of IEnumerable).

To see what is actually going on, we will go through some examples and log the SQL that is actually passed to SQL Server, for which we will use the DataContext.Log property. If we were to run the SQL-like LINQ query above you would find the SQL to be something like the following:

regularquerylog

This is what you would expect to see; however, if you were to use the extension method we defined above, FilterByType, you may be surprised to see:

filterbytypelog

So what happened? Well, since the extension method we wrote was defined for an IEnumerable, the .Net framework chose the IEnumerable version of LINQ’s Where extension method. Consequently, all records from the Vehicle table will be returned and loaded as LINQ objects into an IEnumerable collection. At this point each object would be enumerated over to see if they meet the qualifications of the predicate we passed to the Where method. As you might expect, this is far from performant and could become a large bottleneck within your application.

So, now what? Well the obvious answer would be to simply change the extension method to be for IQueryable<Vehicle> instead of IEnumerable<Vehicle>. The problem with this is now you have an API that is less than friendly to use if you have a non-IQueryable collection, for instance a List<Vehicle>. Instead, I would propose that you have an extension method defined for both the IQueryable and IEnumerable collections, after all this is what the .Net framework does. Your comment now would naturally be, “Wait, I thought we were trying to avoid duplicate code”. Well, you’re right; we are trying to avoid duplicate code, which is why we will be employing the AsQueryable extension method within the IEnumerable version of the extension method.

asqueryable

The runtime cost of using AsQueryable is virtually nothingĀ  and now you can use your logic for both IEnumerable and IQueryable collections. You now also have the opportunity to optimize the queries for a particular type of collection if possible.

kick it on DotNetKicks.com

About these ads

2 Responses so far »

  1. 1

    Composable and Efficient LINQ Queries…

    Thank you for submitting this cool story – Trackback from DotNetShoutout…

  2. 2

    Good catch. Another catch we can add here is the difference between Expression<Func> and just Func. First one is used with IQueryable LINQ extensions the second is used with IEnumerable LINQ extensions.

    Another thing, such approach (Extension methods) will not work with LINQ to Entities.


Comment RSS · TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: