sheryever
8/26/2017 - 1:54 AM

Build Where Clause Dynamically in Linq

Build Where Clause Dynamically in Linq

//Build Where Clause Dynamically in Linq

//https://www.codeproject.com/Tips/582450/Build-Where-Clause-Dynamically-in-Linq

public class Person 
{
     public string Name        { get ; set ; }
     public string Surname     { get ; set ; }
     public int    Age         { get ; set ; }
     public string City        { get ; set ; }
     public double Salary      { get ; set ; }
     public bool   IsHomeOwner { get ; set ; }
}

public static class ExpressionBuilder
{
    private static MethodInfo containsMethod = typeof(string).GetMethod("Contains" );
    private static MethodInfo startsWithMethod =
    typeof(string).GetMethod("StartsWith", new Type [] {typeof(string)});
    private static MethodInfo endsWithMethod =
    typeof(string).GetMethod("EndsWith", new Type [] { typeof(string)});


    public static Expression<Func<T,
    bool >> GetExpression<T>(IList<Filter> filters)
    {
        if  (filters.Count == 0)
            return null ;

        ParameterExpression param = Expression.Parameter(typeof (T), "t" );
        Expression exp = null ;

        if  (filters.Count == 1)
            exp = GetExpression<T>(param, filters[0]);
        else  if  (filters.Count == 2)
            exp = GetExpression<T>(param, filters[0], filters[1]);
        else
        {
            while  (filters.Count > 0)
            {
                var  f1 = filters[0];
                var  f2 = filters[1];

                if  (exp == null )
                    exp = GetExpression<T>(param, filters[0], filters[1]);
                else
                    exp = Expression.AndAlso(exp, GetExpression<T>(param, filters[0], filters[1]));

                filters.Remove(f1);
                filters.Remove(f2);

                if  (filters.Count == 1)
                {
                    exp = Expression .AndAlso(exp, GetExpression<T>(param, filters[0]));
                    filters.RemoveAt(0);
                }
            }
        }

        return Expression.Lambda<Func<T, bool>>(exp, param);
    }

    private static Expression GetExpression<T>(ParameterExpression param, Filter filter)
    {
        MemberExpression member = Expression.Property(param, filter.PropertyName);
        ConstantExpression constant = Expression.Constant(filter.Value);

        switch (filter.Operation)
        {
            case  Op.Equals:
                return Expression.Equal(member, constant);

            case  Op.GreaterThan:
                return Expression.GreaterThan(member, constant);

            case Op.GreaterThanOrEqual:
                return Expression.GreaterThanOrEqual(member, constant);

            case Op.LessThan:
                return Expression.LessThan(member, constant);

            case Op.LessThanOrEqual:
                return Expression.LessThanOrEqual(member, constant);

            case Op.Contains:
                return Expression.Call(member, containsMethod, constant);

            case Op.StartsWith:
                return Expression.Call(member, startsWithMethod, constant);

            case Op.EndsWith:
                return Expression.Call(member, endsWithMethod, constant);
        }

        return null ;
    }

    private static BinaryExpression GetExpression<T>
    (ParameterExpression param, Filter filter1, Filter  filter2)
    {
        Expression bin1 = GetExpression<T>(param, filter1);
        Expression bin2 = GetExpression<T>(param, filter2);

        return  Expression.AndAlso(bin1, bin2);
    }
}

// And the filter class has been extended to take a comparison operation as well:
public class Filter
{
    public string PropertyName { get ; set ; }
    public Op Operation { get ; set ; }
    public object Value { get ; set ; }
}

///And the operation is declared as enumeration:
public enum Op
{
    Equals,
    GreaterThan,
    LessThan,
    GreaterThanOrEqual,
    LessThanOrEqual,
    Contains,
    StartsWith,
    EndsWith
}

//Then the new generic expression builder would be used as follows:
List<Filter> filter = new List<Filter>()
{
    new Filter { PropertyName = "City" ,
        Operation = Op .Equals, Value = "Mitrovice"  },
    new Filter { PropertyName = "Name" ,
        Operation = Op .StartsWith, Value = "L"  },
    new Filter { PropertyName = "Salary" ,
        Operation = Op .GreaterThan, Value = 9000.0 }
};

var deleg = ExpressionBuilder.GetExpression<Person>(filter).Compile();
var filteredCollection = persons.Where(deleg).ToList();