Click here to Skip to main content
Click here to Skip to main content

Expression Tree Basics

, 20 Sep 2012 CPOL
Rate this:
Please Sign up or sign in to vote.
Using C# Expression Trees to build a custom sort routine

Introduction

I expect the reader to be familiar with C# Generics and Reflection.

Some days ago, in the project I am currently working on, I encountered an interesting problem. The problem was to sort a list of objects based on some property.

Let's say we have a Person class:

public class Person
{
	public string Name { get; set; }
	public int Age { get; set; }
	public DateTime JoiningDate { get; set; }
}

And a list of Person objects:

Person p1 = new Person { Name = "Kayes", Age = 29, JoiningDate = DateTime.Parse("2010-06-06") };
Person p2 = new Person { Name = "Gibbs", Age = 34, JoiningDate = DateTime.Parse("2008-04-23") };
Person p3 = new Person { Name = "Steyn", Age = 28, JoiningDate = DateTime.Parse("2011-02-17") };

List<Person> persons = new List<Person>();
persons.Add(p1);
persons.Add(p2);
persons.Add(p3);

Suppose we need to sort the persons list by Age in ascending order. Now, any C# 3.0 rookie (like me) would do this in the following way:

List<Person> sortedList = persons.OrderBy<Person, int>(p => p.Age).ToList();

The OrderBy() would sort the list in ascending order by the Age property and the persons list would look like this:

ExpressionTreeBasics/ExpressionTreeBasics01.jpg

So where is the problem? Everything is working fine here! Right. The problem arose when I realized that the property I need to sort the list by is determined at runtime. How so? I had my persons list projected on an HTML gridview where each property of the Person object is shown in each column of the grid. Now, my client wants to click on the column header to sort the grid data by that column, i.e., the corresponding property of the Person object. Say, the client clicks on the Age column. So the grid data (the persons) would be sorted by the Age property.

When the user clicks on the column, I somehow get the name of the property as a string in the code responsible for sorting the grid data. How the name of the property gets sent to the backend code is beyond the scope of this article. So, we will just assume that the property name is available to us and we just need to sort the list of persons by that property.

Background

When we wrote the OrderBy() call above to sort the list, we told the method the type of the property to sort the list by, as a generic type parameter and the name of the property using a lambda expression. Therefore the property and its type are determined at compile time. But we cannot afford to do that because there are 3 different properties of 3 different data types in our Person object. The user may want to sort the grid by any property. So, we somehow have to determine the type and name of the property at runtime. We assumed that we have the name of the property at our disposal. So, identifying the type and using this information to sort the list are what remain to be done.

And along comes Expression Tree to our rescue. Expression Tree is a language feature introduced in C# 3.0. It is used to build lambda expressions dynamically at runtime. More on Expression Trees here.

Let's Cut the Crap and Get Straight Down into the Code

Right. That's what we coders live for. Code. Wink | ;)

We need to build the lambda expression that we used above to call the OrderBy() method. Which is

p => p.Age

p, here, is the Person object that is passed in the lambda expression. So, first we will create the ParameterExpression. A ParameterExpression denotes the input variable to the left hand side of the lambda (=>) operator in the lambda expression.

ParameterExpression pe = Expression.Parameter(typeof(Person), "p");

We are giving the name of the parameter exactly as we did when we hard-coded the lambda expression above. Now to build the body of the lambda expression. And here's how we do it:

Expression<Func<Person, int>> expr = 
	Expression.Lambda<Func<Person, int>>(Expression.Property(pe, "Age"), pe);

Let's analyze the above line. First of all, what is this Func<Person, int> that is passed as the generic type parameter to the Expression type?

The answer: Func<Person, int> is a delegate that encapsulates a method that has one parameter (Person) and returns a value of the type specified by the second parameter (int). The signature is:

public delegate TResult Func<in T, out TResult>( T arg )

Basically the whole point is the expression we are to build reflects a method denoted by Func<Person, int> which is what we passed as a hard-wired lambda expression at the beginning of this article. And now we're just building the same with expression tree.

But how do we know what the delegate looks like? That is to say, how can we tell how many input parameters the delegate would take or whether the delegate would return a value at all? Easy. The thing is we are building an expression that we eventually will use to call the OrderBy() extension method on a IEnumerable<T> object, i.e, our persons list. The signature of OrderBy() is as follows:

public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
	this IEnumerable<TSource> source,
	Func<TSource, TKey> keySelector
)

So that's how we know what the keySelector delegate will look like. TSource is the type of the object, the list of which we are to sort. TKey is the type of the property by which we want to sort the list. Let's resume the rest of the analysis.

Expression<Func<Person, int>> expr = 
	Expression.Lambda<Func<Person, int>>(Expression.Property(pe, "Age"), pe);

The first parameter that the Expression.Lambda() static method takes is the body expression. In our desired lambda expression, the body is just all about returning a property of the Person object that we have access to through the parameter.

We will create the body expression using the Expression.Property() static method which represents accessing a property in an object. We pass the ParameterExpression pe that we created earlier, as the first parameter to the Expression.Property() method and the name of the property as the second parameter.

And then again the ParameterExpression pe as the second parameter to Expression.Lambda() method which is to tell what our input variable to the lambda expression is.

Here's an important issue to note. In the above code snippet, we are creating the expression with explicit types in the generic type declaration for the delegate. We are hard coding the type of the object (Person), the list of which we are going to sort. We are also hard coding the type of the property (int) by which the list would be sorted. The list type might have been known in most cases but we can't afford to hard code the return type of the delegate as we would not know by which type of property the list would be sorted. But later in the article, we are going to wrap our code in a generic method. Therefore we will have the list type and the return type at our disposal through generic type parameters. But for the sake of simplicity for the discussion, let's create the expression with implicit type var.

var expr = Expression.Lambda(Expression.Property(pe, "Age"), pe);

At this point, our expression is done and if we run a quick watch in Visual Studio on expr, it would look like this:

ExpressionTreeBasics/ExpressionTreeBasics02.jpg

Exactly the same as what we have hard-coded at the beginning. Now let's put together all the codes we have written so far:  

Person p1 = new Person { Name = "Kayes", Age = 29, JoiningDate = DateTime.Parse("2010-06-06") };
Person p2 = new Person { Name = "Gibbs", Age = 34, JoiningDate = DateTime.Parse("2008-04-23") };
Person p3 = new Person { Name = "Steyn", Age = 28, JoiningDate = DateTime.Parse("2011-02-17") };

List<Person> persons = new List<Person>();
persons.Add(p1);
persons.Add(p2);
persons.Add(p3);

string sortByProp = "Age";
ParameterExpression pe = Expression.Parameter(typeof(Person), "p");
var expr = Expression.Lambda(Expression.Property(pe, sortByProp), pe);

Note that we kept the name of the property in a variable and used that variable in building the expression.

All we have to do now is use the expression in the OrderBy() method call.

List<Person> sortedList = persons.OrderBy<Person, int>(expr).ToList();

But wait.. what's this?

ExpressionTreeBasics/ExpressionTreeBasics03.jpg

Seems like there's an error:

'System.Collections.Generic.List<CustomSort.Person>' does not contain a 
definition for 'OrderBy' and the best extension method overload 
'System.Linq.Queryable.OrderBy<TSource,TKey>(System.Linq.IQueryable<TSource>, 
System.Linq.Expressions.Expression<System.Func<TSource,TKey>>)' 
has some invalid arguments. 

That is because our persons list is a type of IEnumerable which does not have an OrderBy() overload which takes an expression. What do we do now? We just need to convert our list to a IQueryable<T>

IQueryable<Person> query = persons.AsQueryable();

and call OrderBy() on the IQueryable<Person>. 

List<Person> sortedList = query.OrderBy<Person, int>(expr).ToList();

Therefore, our revised code would look like the following:

Person p1 = new Person { Name = "Kayes", Age = 29, JoiningDate = DateTime.Parse("2010-06-06") };
Person p2 = new Person { Name = "Gibbs", Age = 34, JoiningDate = DateTime.Parse("2008-04-23") };
Person p3 = new Person { Name = "Steyn", Age = 28, JoiningDate = DateTime.Parse("2011-02-17") };

List<Person> persons = new List<Person>();
persons.Add(p1);
persons.Add(p2);
persons.Add(p3);

string sortByProp = "Age";
ParameterExpression pe = Expression.Parameter(typeof(Person), "p");
var expr = Expression.Lambda(Expression.Property(pe, sortByProp), pe);

IQueryable<Person> query = persons.AsQueryable();
List<Person> sortedList = query.OrderBy<Person, int>(expr).ToList();

So we have our expression working with OrderBy(). But are we done yet? Not quite. Why? Because we are still hard-coding the type of the property that we want to sort our list by, as the generic type parameter to the OrderBy() method. We need to somehow determine this type at runtime. And the way to do this is by using reflection in the following way:

Type sortByPropType = typeof(Person).GetProperty(sortByProp).PropertyType;

Now we need to call OrderBy() by passing the type stored in sortByPropType variable. And here, also, reflection comes into play. First we need to get the corresponding MethodInfo object for the static generic OrderBy() method which is an extension method defined in the static Queryable class.

MethodInfo orderByMethodInfo = typeof(Queryable)
			.GetMethods(BindingFlags.Public | BindingFlags.Static)
			.Single(mi => mi.Name == "OrderBy"
			&& mi.IsGenericMethodDefinition
			&& mi.GetGenericArguments().Length == 2
			&& mi.GetParameters().Length == 2
		);

Now that we have our MethodInfo object for the OrderBy() method, we need to invoke it in the following way:

List<Person> sortedList = (orderByMethodInfo.MakeGenericMethod(new Type[] 
	{ typeof(Person), sortByPropType }).Invoke(query, new object[] 
	{ query, expr }) as IOrderedQueryable<Person>).ToList();

The above reflection codes are definitely not easy on the eyes, I know Wink | ;)

But that's it. We have our list sorted by Age in ascending order in the sortedList variable.

Final Code

Let's see what our final code looks like: 

class Program
{
	static void Main(string[] args)
	{
		Person p1 = new Person { Name = "Kayes", Age = 29, JoiningDate = DateTime.Parse("2010-06-06") };
		Person p2 = new Person { Name = "Gibbs", Age = 34, JoiningDate = DateTime.Parse("2008-04-23") };
		Person p3 = new Person { Name = "Steyn", Age = 28, JoiningDate = DateTime.Parse("2011-02-17") };

		List<Person> persons = new List<Person>();
		persons.Add(p1);
		persons.Add(p2);
		persons.Add(p3);

		string sortByProp = "Age";
		Type sortByPropType = typeof(Person).GetProperty(sortByProp).PropertyType;
		
		ParameterExpression pe = Expression.Parameter(typeof(Person), "p");
		var expr = Expression.Lambda(Expression.Property(pe, sortByProp), pe);

		IQueryable<Person> query = persons.AsQueryable();
		
		MethodInfo orderByMethodInfo = 
		typeof(Queryable).GetMethods(BindingFlags.Public | BindingFlags.Static)
				.Single(mi => mi.Name == "OrderBy"
				&& mi.IsGenericMethodDefinition
				&& mi.GetGenericArguments().Length == 2
				&& mi.GetParameters().Length == 2
		);

		List<Person> sortedList = 
			(orderByMethodInfo.MakeGenericMethod(new Type[] 
			{ typeof(Person), sortByPropType }).Invoke(query, 
			new object[] { query, expr }) as 
			IOrderedQueryable<Person>).ToList();
	}
}

Improvements

While our code above works just fine, it is heavily dependent on the Person object. So if need be, for lists of other types of objects we have to write basically the same code with different objects other than Person. So I was thinking of making the code reusable and generic so that it can accommodate any type of object we provide. We can do it by creating an extension method for the IEnumerable type.

public static class MyExtensions
{
	public static List<T> CustomSort<T, TPropertyType>
	(this IEnumerable<T> collection, string propertyName, string sortOrder)
	{
		List<T> sortedlist = null;
		IQueryable<T> query = collection.AsQueryable<T>();

		ParameterExpression pe = Expression.Parameter(typeof(T), "p");
		Expression<Func<T, TPropertyType>> expr = Expression.Lambda<Func<T, TPropertyType>>(Expression.Property(pe, propertyName), pe);
		
		if (!string.IsNullOrEmpty(sortOrder) && sortOrder == "desc")
			sortedlist = query.OrderByDescending<T, TPropertyType>(expr).ToList();
		else
			sortedlist = query.OrderBy<T, TPropertyType>(expr).ToList();

		return sortedlist;
	}
} 

And invoke the extension method using reflection in the following way:

List<Person> sortedList = typeof(MyExtensions).GetMethod("CustomSort").
	MakeGenericMethod(new Type[] { typeof(Person), sortByPropType }).
	Invoke(persons, new object[] { persons, sortByProp, "asc" }) as List<Person>;

And if necessary, we can still call the extension method with hard-coded types as easily as the following way:

sortedList = persons.CustomSort<Person, int>(sortByProp, "desc");

Edit (08-04-2011)

Thanks to Alois Kraus who suggested that there is no need to convert the IEnumerable<T> to IQueryable<T> since we can call the Compile() method on Expression<TDelegate> object which compiles the lambda expression described by the expression tree into executable code and produces a delegate that represents the lambda expression. We can then pass that delegate to IEnumerable<T>.OrderBy() method. So our revised extension method should be:

public static class MyExtensions
{
	public static List<T> CustomSort<T, TPropertyType>
	(this IEnumerable<T> collection, string propertyName, string sortOrder)
	{
		List<T> sortedlist = null;
		
		ParameterExpression pe = Expression.Parameter(typeof(T), "p");
		Expression<Func<T, TPropertyType>> expr = Expression.Lambda<Func<T, TPropertyType>>(Expression.Property(pe, propertyName), pe);
		
		if (!string.IsNullOrEmpty(sortOrder) && sortOrder == "desc")
			sortedlist = collection.OrderByDescending<T, TPropertyType>(expr.Compile()).ToList();
		else
			sortedlist = collection.OrderBy<T, TPropertyType>(expr.Compile()).ToList();

		return sortedlist;
	}
}

Conclusion

Expression Tree is the coolest language feature C# 3.0 introduced. And along with Lambda Expression, which is also a C# 3.0 feature, Expression Trees can open lots of doors to interesting possibilities. I am definitely looking forward to using these two more and more in my future projects.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Abul Kayes
Software Developer (Senior)
Australia Australia
No Biography provided
Follow on   Twitter   LinkedIn

Comments and Discussions

 
QuestionProblem to convert to AsQueryable Pinmemberoyechaman28-Oct-14 6:38 
GeneralMy vote of 3 PinmemberTony496627-Aug-14 0:46 
Generalrealy Great and simple Pinmemberrasul gani19-Feb-14 2:45 
QuestionBrilliant! PinmemberSteveBaldwin214-Jan-14 11:03 
GeneralRe: Brilliant! PinmemberAbul Kayes5-Jan-14 1:16 
Generalgood article on expression tress. Pinmemberramesh_nrk28-Oct-13 23:57 
GeneralIt's useful. PinmemberMember 101846641-Aug-13 23:03 
BugMistake in code? PinmemberRiz Thon17-Aug-11 16:20 
GeneralRe: Mistake in code? PinmemberAbul Kayes17-Aug-11 19:28 
GeneralRe: Mistake in code? PinprotectorAspDotNetDev8-Sep-12 18:33 
GeneralRe: Mistake in code? PinmemberAbul Kayes20-Sep-12 5:05 
GeneralGreat article Pinmembermr0004714-Aug-11 22:30 
QuestionSimplification PinmemberRichard Deeming11-Aug-11 4:29 
AnswerRe: Simplification PinmemberAbul Kayes11-Aug-11 6:59 
QuestionI did simliar article a while back PinmvpSacha Barber9-Aug-11 23:49 
QuestionGreat article Pinmemberpigwin329-Aug-11 12:24 
AnswerRe: Great article PinmemberAbul Kayes11-Aug-11 7:00 
QuestionUse Dynamic Linq PinmemberDaniel Gidman4-Aug-11 9:13 
AnswerRe: Use Dynamic Linq PinmemberAbul Kayes4-Aug-11 9:24 
QuestionNice one but PinmemberAlois Kraus4-Aug-11 6:45 
AnswerRe: Nice one but PinmemberAbul Kayes4-Aug-11 7:40 
QuestionNice one [modified] Pinmemberbilo814-Aug-11 0:46 
AnswerRe: Nice one PinmemberAbul Kayes4-Aug-11 6:33 
GeneralMy vote of 5 PinmemberMd. Rashim uddin4-Aug-11 0:22 
GeneralMy vote of 5 Pinmemberkornakar3-Aug-11 22:52 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141216.1 | Last Updated 20 Sep 2012
Article Copyright 2011 by Abul Kayes
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid