Wednesday 30 December 2015

Dynamic LINQ OrderBy using String Names!

LINQ is a new enhancement to the .NET framework (version 3.5 and up) that gives powerful query capabilities against any enumerable object. This means that no matter what the data source (whether SQL, XML, or objects such as a generic list or ArrayList) you can do things like sorting (OrderBy), selecting, joining etc.Quick Overview of LINQ and Lambda Expressions
If you're not already familiar with LINQ, then you can Learn the Basics of LINQ from that article. Also, to appreciate this new gem that I discovered (actually, I've been trying to do this since I first started using LINQ), you'll need to understand lambda expressions with LINQ.

How To Sort using LINQ and Lamdba

First, let's look at a quick example of a data source that you would possibly want to sort, perhaps in a GridView. For the sake of simplicity, I'm just going to populate a generic list in code. If the code that I'm using looks funny, it's probably because you're not familiar with automatic properties (a new C# 3 only feature), lambda (a new language feature in C# 3 and VB9) or anonymous types.
Try not to get lost in the code about how to build my person class. The good stuff is at the end with the dyanmic LINQ! Here is my basic "Person" class that will hold our data:
public class Person
{
   public string FirstName { getset; }
   public string LastName { getset; }
   public DateTime DateOfBirth { getset; }
}
Now I'll create a list with 3 people in there. Then I'll show how to sort it using regular old LINQ. After that, I'll show you how to make your own Expression Tree to dynamically sort it using LINQ (it's exciting)! By the way, it took a lot of reverse engineering and reflecting to figure this out... and in case you're wondering, this is NO WHERE else on the internet right now.
// Here's my list that I am going to sort.
List<Person> people = new List<Person>();

people.Add(new Person { FirstName = "Moses",
   LastName = "McRoses",
   DateOfBirth = new DateTime(1801, 1, 23) });

people.Add(new Person { FirstName = "Timothy",
   LastName = "Khouri",
   DateOfBirth = new DateTime(1985, 6, 20) });

people.Add(new Person { FirstName = "Jonathan",
   LastName = "Carter",
   DateOfBirth = new DateTime(1984, 6, 12) });
As you can see, our list has 3 people that are not in any particular order. Now, I'll sort them by age:
// To do this, you have to have using System.Linq and must
// reference the System.Core assembly.


Person[] sortedPeople = people.OrderBy(person =>
   person.DateOfBirth).ToArray();
That was simple enough. But what if you bound your "people" list to a GridView that had sorting turned on. The problem here is that you are going to get a STRING that is the NAME of the field that the user wants to sort by. So what do we do?
// This won't work!

Person[] sortedPeople = people.OrderBy(person =>
   "DateOfBirth").ToArray();
The reason why the above won't work is because you're trying to sort by a literal string "DateOfBirth". Since "DateOfBirth" and "DateOfBirth" and "DateOfBirth" are all the same words, then LINQ will ignore this OrderBy alltogether. To achieve what we want, you have to create your own expression tree.
// First we define the parameter that we are going to use
// in our OrderBy clause. This is the same as "(person =>"

// in the example above.

var param = Expression.Parameter(typeof(Person), "person");

// Now we'll make our lambda function that returns the

// "DateOfBirth" property by it's name.

var mySortExpression = Expression.Lambda<Func<Person, object>>(Expression.Property(param, "DateOfBirth"), param);

// Now I can sort my people list.

Person[] sortedPeople = people.OrderBy(mySortExpression).ToArray();

Dynamic Expression Explaination

The code sample above does exactly what the original lambda expression was doing. The reason why I chose to do "Person, object" for my types instead of "Person, DateTime" is because if this was truely dynamic, I wouldn't know the data type of the column being sorted.
Actually, I should mention that I also wouldn't know the "Person" datatype, so I'd have to use "object, object"... but I'm not going to get into that right now as you'd have to do a pretty hefty chunk of reflection to get the right parameters.
The more I look into LINQ and expression trees, the more I like it. This stuff may look daunting at first, but when you "get it", you'll really see and appreciate the power it brings.

No comments:

Post a Comment

Angular Tutorial (Update to Angular 7)

As Angular 7 has just been released a few days ago. This tutorial is updated to show you how to create an Angular 7 project and the new fe...