Friday, December 26, 2014

How to build dynamic lambda expression

I recently came across a requirement where I had to filter entity objects. Consider the following scenario:

I have a class called - Product.

public class Product
{
    public string Name { get; set; }
    public string Code { get; set; }
    public string Type { get; set; }
    public bool IsTaxable { get; set; }

    public Product(string name, string code, bool isTaxable)
    {
        this.Name = name;
        this.Code = code;
        this.IsTaxable = isTaxable;
    }

}

Let’s first create list of products and populate it.

List<Product> products = new List<Product>();
products.Add(new Product("ABC", "a", true));
products.Add(new Product("PQR", "p", false));
products.Add(new Product("XYZ", "x", true));

If I want to filter Products which are taxable, I can just get them by using the following Lambda expression statement:
List<Product> filteredList = EntityFilter.FilterRecords(prods, "IsTaxable", true).ToList();


Very easy, isn’t it?

The above requirement is pretty much straightforward. Let’s go beyond it. Consider, you’re making a search webpart and you need to allow user to filter products based on any attribute. They should be able to filter by Name or Code or any other attribute. In this scenario, you don’t know on which attribute the user is going to filter the records.

So will you write the code as follows?

if (attributeName == "Name")
    filteredProducts = products.FindAll(p => p.Name == attributeValue);
else if (attributeName == "Code")
    filteredProducts = products.FindAll(p => p.Code == attributeValue);
else if (attributeName == "IsTaxable")
    filteredProducts = products.FindAll(p => p.IsTaxable == Convert.ToBoolean(attributeValue));

Don’t do this!!  You’ll end up making a mess of your existing code.

Then how shall we proceed? Well, this is where the blog starts:

Let’s create a class, called “EntityFilter”.

public class EntityFilter
{
    public static IQueryable<T> FilterRecords<T>(IQueryable<T> source, string propertyName, object value)
    {
        ParameterExpression parameter = Expression.Parameter(typeof(T));
        Expression predicate = Expression.Constant(true);

        Expression expProperty = Expression.PropertyOrField(parameter, propertyName);
        Expression expFilter = Expression.Equal(expProperty, Expression.Constant(value));
        var lambda = Expression.Lambda<Func<T, bool>>(expFilter, parameter);
        return source.Where(lambda);
    }
}


That’s all. You’re done. 
Now just fire a following statement to filter records:

filteredProducts = EntityFilter.FilterRecords(products.AsQueryable(), "Name", "ABC").ToList();

OR

filteredProducts = EntityFilter.FilterRecords(products.AsQueryable(), "Code", "x").ToList();

OR

filteredProducts = EntityFilter.FilterRecords(products.AsQueryable(), "IsTaxable", true).ToList();

  
Looks amazing, isn't it?

Let's go ahead and dissect each statement one at a time.

You first start off with a parameter expression of type “T”. Here, “T” can be any type or class:
ParameterExpression parameter = Expression.Parameter(typeof(T));

The next line is to define, predicate:
Expression predicate = Expression.Constant(true);

The next step is to build the body of lambda expressions which happens to be property expression and filter:
Expression expProperty = Expression.PropertyOrField(parameter, propertyName);
Expression expFilter = Expression.Equal(expProperty, Expression.Constant(value));


Now, we need to build the lambda expression which combines the body with the parameter as follows:
var lambda = Expression.Lambda<Func<T, bool>>(expFilter, parameter);


The final step is to filter the source with the dynamic lambda expression:
return source.Where(lambda);

Isn’t this completely dynamic? You can filter any entity with any attribute. Why not have this utility class in every solution you build!

There are always different ways to write code but there is only one way to write awesome code!!