Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
C#: LINQ Expression Builder dynamic where clause based on filters
public class ExpressionBuilder
{
// Define some of our default filtering options
private static MethodInfo containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
private static MethodInfo startsWithMethod = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
private static MethodInfo endsWithMethod = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
public static Expression<Func<T, bool>> GetExpression<T>(List<GridHelper.Filter> filters)
{
// No filters passed in #KickIT
if (filters.Count == 0)
return null;
// Create the parameter for the ObjectType (typically the 'x' in your expression (x => 'x')
// The "parm" string is used strictly for debugging purposes
ParameterExpression param = Expression.Parameter(typeof(T), "parm");
// Store the result of a calculated Expression
Expression exp = null;
if (filters.Count == 1)
exp = GetExpression<T>(param, filters[0]); // Create expression from a single instance
else if (filters.Count == 2)
exp = GetExpression<T>(param, filters[0], filters[1]); // Create expression that utilizes AndAlso mentality
else
{
// Loop through filters until we have created an expression for each
while (filters.Count > 0)
{
// Grab initial filters remaining in our List
var f1 = filters[0];
var f2 = filters[1];
// Check if we have already set our Expression
if (exp == null)
exp = GetExpression<T>(param, filters[0], filters[1]); // First iteration through our filters
else
exp = Expression.AndAlso(exp, GetExpression<T>(param, filters[0], filters[1])); // Add to our existing expression
filters.Remove(f1);
filters.Remove(f2);
// Odd number, handle this seperately
if (filters.Count == 1)
{
// Pass in our existing expression and our newly created expression from our last remaining filter
exp = Expression.AndAlso(exp, GetExpression<T>(param, filters[0]));
// Remove filter to break out of while loop
filters.RemoveAt(0);
}
}
}
return Expression.Lambda<Func<T, bool>>(exp, param);
}
private static Expression GetExpression<T>(ParameterExpression param, GridHelper.Filter filter)
{
// The member you want to evaluate (x => x.FirstName)
MemberExpression member = Expression.Property(param, filter.PropertyName);
// The value you want to evaluate
ConstantExpression constant = Expression.Constant(filter.Value);
// Determine how we want to apply the expression
switch (filter.Operator)
{
case GridHelper.Operator.Equals:
return Expression.Equal(member, constant);
case GridHelper.Operator.Contains:
return Expression.Call(member, containsMethod, constant);
case GridHelper.Operator.GreaterThan:
return Expression.GreaterThan(member, constant);
case GridHelper.Operator.GreaterThanOrEqual:
return Expression.GreaterThanOrEqual(member, constant);
case GridHelper.Operator.LessThan:
return Expression.LessThan(member, constant);
case GridHelper.Operator.LessThanOrEqualTo:
return Expression.LessThanOrEqual(member, constant);
case GridHelper.Operator.StartsWith:
return Expression.Call(member, startsWithMethod, constant);
case GridHelper.Operator.EndsWith:
return Expression.Call(member, endsWithMethod, constant);
}
return null;
}
private static BinaryExpression GetExpression<T>(ParameterExpression param, GridHelper.Filter filter1, GridHelper.Filter filter2)
{
Expression result1 = GetExpression<T>(param, filter1);
Expression result2 = GetExpression<T>(param, filter2);
return Expression.AndAlso(result1, result2);
}
}
public ActionResult Grid()
{
//....
//Implementing ExpressionBuilder
Expression<Func<Contact, bool>> deleg = ExpressionBuilder.GetExpression<Contact>(filters);
_contacts = _contacts.Where(deleg);
//.....
}
// Filter class
public class GridHelper
{
public enum Operator
{
Contains,
GreaterThan,
GreaterThanOrEqual,
LessThan,
LessThanOrEqualTo,
StartsWith,
EndsWith,
Equals,
NotEqual
}
public class Filter
{
public string PropertyName { get; set; }
public string Value { get; set; }
private Operator _op = Operator.Contains;
public Operator Operator
{
get
{
return _op;
}
set
{
_op = value;
}
}
}
public class Filters : List<GridHelper.Filter>
{
public Filters(params string[] args)
{
HttpContext context = System.Web.HttpContext.Current;
foreach (var arg in args)
{
this.Add(arg, context.Request.Form[arg] ?? "");
}
}
public void Add(string Name, string Value = "")
{
this.Add(new Filter(){ PropertyName = Name, Value = Value});
}
}
}
}
@gvii

This comment has been minimized.

Copy link

gvii commented Jul 22, 2015

Hi, I am trying to use this code and I would like to make the "equals" operation case insensitive.

case GridHelper.Operator.Equals:
                return Expression.Equal(member, constant);

Would you pls. let me know how would I go about it?

Thanks in advance

@emrebostan

This comment has been minimized.

Copy link

emrebostan commented Dec 18, 2017

i love this code

@iloshkakartochka

This comment has been minimized.

Copy link

iloshkakartochka commented May 24, 2018

Contains, does not work if there is case sensitive data. So i made a slight change to contains, in case anyone needs it. Basically, use indexof

var pi = param.Type.GetProperty(filter.PropertyName);
var propertyAccess = Expression.MakeMemberAccess(param, pi);
var indexOf = Expression.Call(propertyAccess, "IndexOf", null, Expression.Constant(constant.Value, typeof(string)), Expression.Constant(StringComparison.InvariantCultureIgnoreCase));
return Expression.GreaterThanOrEqual(indexOf, Expression.Constant(0));

@arunzer0

This comment has been minimized.

Copy link

arunzer0 commented Aug 13, 2018

Really Such a nice code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.