Click here to Skip to main content
15,884,629 members
Articles / Programming Languages / C#

Dynamic Linq Queries

Rate me:
Please Sign up or sign in to vote.
4.60/5 (2 votes)
7 Jun 2009MIT2 min read 37.4K   24   4
Alright, let’s assume that we are lazy coders, we have building a lot of Linq queries lately, and it’s getting repetitive. We keep having to remember to add a certain where clause to every query, couldn’t we just abstract this somehow?

Alright, let’s assume that we are lazy coders, we are building a lot of Linq queries lately, and it’s getting repetitive. We keep having to remember to add a certain where clause to every query, couldn’t we just abstract this somehow? Well sure, we can use Expressions!

Let’s first take a look at the System.Linq.Queryable.Where extension method, here is the method’s signature:

C#
public static IQueryable<TSource> Where<TSource>(
	this IQueryable<TSource> source, 
	Expression<Func<TSource, bool>> predicate);

Notice that it takes a lambda expression, which we can generate dynamically. For examples sake, let’s make a Foo and a Bar class, and two methods that generate a list of each:

C#
static IEnumerable<Foo> MakeFoos() { 
	for (int i = 0; i < 10; i++) 
		yield return new Foo { A = "Foo " + i, B = i }; 
} 
C#
static IEnumerable<Bar> MakeBars() { 
	for (int i = 0; i < 10; i++) 
		yield return new Bar { A = "Bar " + i, B = i }; 
}
C#
class Foo { 
	public string A { get; set; } 
	public int B { get; set; } 
} 
C#
class Bar { 
	public string A { get; set; } 
	public int B { get; set; } 
}

So what if we want to get all of the items in a collection with A and even B? What we need is an extension method that applies our filter to an IQueryable<> of any type. First off, let’s look at the method signature:

C#
public static IQueryable<T> FilterB<T>(this IQueryable<T> query)

It’s fairly simple, it is an extension method that takes an IQueryable<T> and returns an IQueryable<T> so that we can keep our fluent interface going. The first thing we have to do in our method is create a parameter and property expression.

C#
var param = Expression.Parameter(typeof(T), "x"); 
var prop = Expression.Property(param, "B");

Then we need a binary expression that %’s our property by 2:

C#
var two = Expression.Constant(2); 
var mod = Expression.MakeBinary(ExpressionType.Modulo, prop, two);

The ConstantExpressions are pretty self explanatory, they just hold the value that they are given. BinaryExpressions can represent any kind of binary expression, which is any expression that has a left and right side, like a == b, a != b, a + b, and b – a for example. Next, we need to make another binary expression that checks to see if our first binary expression, mod, is equal to zero:

C#
var zero = Expression.Constant(0) ; 
var eqZero = Expression.MakeBinary(ExpressionType.Equal, mod, zero);

Notice that we used one binary expression as a parameter for another, MakeBinary’s second and third parameters are just Expressions which can be any type of expression. All that is left to do is create the lambda expression, where it and return it:

C#
var lambda = Expression.Lambda<Func<T, bool>>(eqZero, param); 
return query.Where(lambda);

We don’t have to compile the lambda here because query providers use it in its raw expression tree form which they translate into whatever they need to get the job done. Here is the entire method:

C#
public static IQueryable<T> FilterB<T>(this IQueryable<T> query) { 
	var param = Expression.Parameter(typeof(T), "x"); 
	var prop = Expression.Property(param, "B"); 
	var two = Expression.Constant(2); 
	var mod = Expression.MakeBinary(ExpressionType.Modulo, prop, two); 
	var zero = Expression.Constant(0); 
	var eqZero = Expression.MakeBinary(ExpressionType.Equal, mod, zero); 
	var lambda = Expression.Lambda<Func<T, bool>>(eqZero, param); 
	return query.Where(lambda); 
}

This should work with any query provider including Linq to SQL and the Entity Framework. We haven’t built in any error checking so expect it to throw if you use it on a collection of a type that does not have a B property. So, given the types we defined above, let’s see how we would use it:

C#
var foos = MakeFoos().AsQueryable(); 
var bars = MakeBars().AsQueryable(); 
foreach (var foo in foos.FilterB()) 
Console.WriteLine(foo.A); 
foreach (var bar in bars.FilterB()) 
Console.WriteLine(bar.A);

We are first getting collections of both Foos and Bars, and then changing them into IQueryable collections. We then just iterate over each collection after calling FilterB() on them. We could chain other calls after the FilterB call if we wanted. The output will be:

Foo 0 
Foo 2 
Foo 4 
Foo 6 
Foo 8 
Bar 0 
Bar 2 
Bar 4 
Bar 6 
Bar 8 

Yay! It works!

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Software Developer
United States United States
I currently work as a Software Engineer for a company in North Carolina, mainly working with C#.

Comments and Discussions

 
GeneralUnreadable Pin
tonyt5-Jun-09 20:44
tonyt5-Jun-09 20:44 
GeneralRe: Unreadable Pin
StormySpike7-Jun-09 16:04
StormySpike7-Jun-09 16:04 
GeneralRe: Unreadable Pin
StormySpike7-Jun-09 16:59
StormySpike7-Jun-09 16:59 
GeneralRe: Unreadable Pin
User 69679843-Oct-12 9:18
User 69679843-Oct-12 9:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.