Monday, October 7, 2013

Filter an IQueryable Datasource on the Fly using Expression Tree

LINQ(Language Integrated Query) is a wonderful language of Microsoft.net Framework. Now a days most of the .net developer using it in their project. LINQ has so many benefits to use. However- there are few limitation of LINQ too. One of the major limitation of LINQ is – lacking of generating LINQ expression dynamically. I know this is probably not a standard feature of any programming language but this is often needed when we use LINQ with other languages. However- there are several solutions exists for this problem. Most acceptable solution probably is using Expression Tree.

Problem Description:
Say, we have following LINQ Query:

image

We like to filter out all the tickets that has expire date and not currently selected by user. User’s selection (selected item ID) will be send to the function dynamically through the function parameter. Therefore, we want to create a expression tree which will generate a LINQ query like below:

image

Solution:
Let’s create the expression tree step by step:

1. Do not forget to add namespace Winking smile

image

2. Define the  column name and data type to filter

image

3. Initialize the Binary Expression and Lamda Expression

image

4. If the selected item is passed through the function parameter, keep it in the exception list and create the lambda expression dynamically based on it.

image

5. Now, we have everything to build the where clause

image

6. Return the result

image

Full Source Code:
Below is the full source code at a glance:

///
/// Filter expired items(ignores selected item) from an IQueryable datasource.
/// Assumption: Datasource should have a column "Expire_Date"
///
/// IQueryable Datasource
/// Data Value Field
/// Selected Value
/// IQueryable Data Items

    public static IQueryable GetFilteredData(IQueryable dataSource, string dataValueField = null, object selectedValue = null)
    {
        try
        {
            //filtered items
            string expiredDateColumnName = "Expire_Date";
            ParameterExpression p = Expression.Parameter(dataSource.ElementType, "p");
            MemberExpression prop = Expression.Property(p, expiredDateColumnName);

            var value = Expression.Constant(null, typeof(DateTime?));

            BinaryExpression binaryExpr1 = Expression.Equal(prop, value);
            LambdaExpression pred = null;

            if (selectedValue != null &&
                !string.IsNullOrEmpty(dataValueField)
                )
            {
                var selectedValueExpr = Expression.Constant(selectedValue, selectedValue.GetType());

                //ignore selected item
                MemberExpression memberExpr2 = Expression.Property(p, dataValueField);
                Expression binaryExpr2 = Expression.Equal(memberExpr2, selectedValueExpr);

                Expression orExpr = Expression.OrElse(binaryExpr1, binaryExpr2);
                pred = Expression.Lambda(orExpr, p);
            }
            else
            {
                pred = Expression.Lambda(binaryExpr1, p);
            }

            var where = Expression.Call(typeof(Queryable), "Where", new[] { dataSource.ElementType }, dataSource.Expression, pred);


            var result = dataSource.Provider.CreateQuery(where);
            return result;
        }
        catch
        {
            throw;
        }
    }



Happy Programming!!!

No comments: