-
-
Save afreeland/6733381 to your computer and use it in GitHub Desktop.
| 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}); | |
| } | |
| } | |
| } | |
| } |
i love this code
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));
Really Such a nice code
This doesn't work for Nullable Data types like datetime,int for operators <, > >=,Please suggest
How can we handle nullable column.?
age column is nullable and i want to search LIKE operator how can we search?
Example: Select * from User where age like '%10%'
Contains workaround for EF .net postgres:
Declare
private static MethodInfo? containsMethod = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
private static MethodInfo? toLowerMethod = typeof(string).GetMethod("ToLower", new Type[0]);
Then
case FilterOperationEnum.Contains:
var memeberToLower = Expression.Call(member, toLowerMethod);
return Expression.Call(memeberToLower, containsMethod, actualValue);
break;
@thummartechnolab
I recently had the same issue you were facing. To handle NULLs, change lines 63 & 64 to:
// make sure we can convert from string for comparison
var propertyType = ((PropertyInfo)member.Member).PropertyType;
var converter = TypeDescriptor.GetConverter(propertyType);
if (!converter.CanConvertFrom(typeof(string)))
throw new NotSupportedException();
// The value that you want to evaluate, converted
var convertedFilterValue = converter.ConvertFromInvariantString(filter.Value);
// The value you want to evaluate
ConstantExpression constant = Expression.Constant(convertedFilterValue);
//ConstantExpression constant = Expression.Constant(filter.Value);
UnaryExpression valueExpression = Expression.Convert(constant, propertyType);
And then, when you do your Expression.Equal, etc, use valueExpression instead of constant. Effectively, make sure that your incoming filter.Value, which is currently string, can be converted to the type of the field in the target data source / database; and if so, build up a conversion expression and use that.
Hi, I am trying to use this code and I would like to make the "equals" operation case insensitive.
Would you pls. let me know how would I go about it?
Thanks in advance