Enhanced Dynamic Filtering
Posted
by Ricardo Peres
on ASP.net Weblogs
See other posts from ASP.net Weblogs
or by Ricardo Peres
Published on Fri, 28 May 2010 22:59:06 GMT
Indexed on
2010/05/28
23:02 UTC
Read the original article
Hit count: 559
Remember my last post on dynamic filtering?
Well, this time I'm extending the code in order to allow two levels of querying:
Match type, represented by the following options:
public enum MatchType { StartsWith = 0, Contains = 1 }
And word match:
public enum WordMatch { AnyWord = 0, AllWords = 1, ExactPhrase = 2 }
You can combine the two levels in order to achieve the following combinations:
MatchType.StartsWith + WordMatch.AnyWord | Matches any record that starts with any of the words specified |
MatchType.StartsWith + WordMatch.AllWords | Not available: does not make sense, throws an exception |
MatchType.StartsWith + WordMatch.ExactPhrase | Matches any record that starts with the exact specified phrase |
MatchType.Contains + WordMatch.AnyWord | Matches any record that contains any of the specified words |
MatchType.Contains + WordMatch.AllWords | Matches any record that contains all of the specified words |
MatchType.Contains + WordMatch.ExactPhrase | Matches any record that contains the exact specified phrase |
Here is the code:
public static IList Search(IQueryable query, Type entityType, String dataTextField, String phrase, MatchType matchType, WordMatch wordMatch, Int32 maxCount) { String [] terms = phrase.Split(' ').Distinct().ToArray(); StringBuilder result = new StringBuilder(); PropertyInfo displayProperty = entityType.GetProperty(dataTextField); IList searchList = null; MethodInfo orderByMethod = typeof(Queryable).GetMethods(BindingFlags.Public | BindingFlags.Static).Where(m => m.Name == "OrderBy").ToArray() [ 0 ].MakeGenericMethod(entityType, displayProperty.PropertyType); MethodInfo takeMethod = typeof(Queryable).GetMethod("Take", BindingFlags.Public | BindingFlags.Static).MakeGenericMethod(entityType); MethodInfo whereMethod = typeof(Queryable).GetMethods(BindingFlags.Public | BindingFlags.Static).Where(m => m.Name == "Where").ToArray() [ 0 ].MakeGenericMethod(entityType); MethodInfo distinctMethod = typeof(Queryable).GetMethods(BindingFlags.Public | BindingFlags.Static).Where(m => m.Name == "Distinct" && m.GetParameters().Length == 1).Single().MakeGenericMethod(entityType); MethodInfo toListMethod = typeof(Enumerable).GetMethod("ToList", BindingFlags.Static | BindingFlags.Public).MakeGenericMethod(entityType); MethodInfo matchMethod = typeof(String).GetMethod ( (matchType == MatchType.StartsWith) ? "StartsWith" : "Contains", new Type [] { typeof(String) } ); MemberExpression member = Expression.MakeMemberAccess ( Expression.Parameter(entityType, "n"), displayProperty ); MethodCallExpression call = null; LambdaExpression where = null; LambdaExpression orderBy = Expression.Lambda ( member, member.Expression as ParameterExpression ); switch (matchType) { case MatchType.StartsWith: switch (wordMatch) { case WordMatch.AnyWord: call = Expression.Call ( member, matchMethod, Expression.Constant(terms [ 0 ]) ); where = Expression.Lambda ( call, member.Expression as ParameterExpression ); for (Int32 i = 1; i < terms.Length; ++i) { String term = terms [ i ]; MethodCallExpression call2 = Expression.Call ( member, matchMethod, Expression.Constant(term) ); LambdaExpression where2 = Expression.Lambda ( call2, member.Expression as ParameterExpression ); var exp = Expression.Invoke(where2, where.Parameters.Cast()); where = Expression.Lambda ( Expression.Or ( where.Body, exp ), where.Parameters.ToArray() ); } break; case WordMatch.ExactPhrase: call = Expression.Call ( member, matchMethod, Expression.Constant(phrase) ); where = Expression.Lambda ( call, member.Expression as ParameterExpression ); break; case WordMatch.AllWords: throw (new Exception("The match type StartsWith is not supported with word match AllWords")); } break; case MatchType.Contains: switch (wordMatch) { case WordMatch.AnyWord: call = Expression.Call ( member, matchMethod, Expression.Constant(terms [ 0 ]) ); where = Expression.Lambda ( call, member.Expression as ParameterExpression ); for (Int32 i = 1; i < terms.Length; ++i) { String term = terms [ i ]; MethodCallExpression call2 = Expression.Call ( member, matchMethod, Expression.Constant(term) ); LambdaExpression where2 = Expression.Lambda ( call2, member.Expression as ParameterExpression ); var exp = Expression.Invoke(where2, where.Parameters.Cast ()); where = Expression.Lambda ( Expression.Or ( where.Body, exp ), where.Parameters.ToArray() ); } break; case WordMatch.ExactPhrase: call = Expression.Call ( member, matchMethod, Expression.Constant(phrase) ); where = Expression.Lambda ( call, member.Expression as ParameterExpression ); break; case WordMatch.AllWords: call = Expression.Call ( member, matchMethod, Expression.Constant(terms [ 0 ]) ); where = Expression.Lambda ( call, member.Expression as ParameterExpression ); for (Int32 i = 1; i < terms.Length; ++i) { String term = terms [ i ]; MethodCallExpression call2 = Expression.Call ( member, matchMethod, Expression.Constant(term) ); LambdaExpression where2 = Expression.Lambda ( call2, member.Expression as ParameterExpression ); var exp = Expression.Invoke(where2, where.Parameters.Cast ()); where = Expression.Lambda ( Expression.AndAlso ( where.Body, exp ), where.Parameters.ToArray() ); } break; } break; } query = orderByMethod.Invoke(null, new Object [] { query, orderBy }) as IQueryable; query = whereMethod.Invoke(null, new Object [] { query, where }) as IQueryable; if (maxCount != 0) { query = takeMethod.Invoke(null, new Object [] { query, maxCount }) as IQueryable; } searchList = toListMethod.Invoke(null, new Object [] { query }) as IList; return (searchList); }
And this is how you'd use it:
IQueryable query = ctx.MyEntities; IList list = Search(query, typeof(MyEntity), "Name", "Ricardo Peres", MatchType.Contains, WordMatch.ExactPhrase, 10 /*0 for all*/);
© ASP.net Weblogs or respective owner