Enhanced Dynamic Filtering
- by Ricardo Peres
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 ());
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 ());
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 ());
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*/);
SyntaxHighlighter.config.clipboardSwf = 'http://alexgorbatchev.com/pub/sh/2.0.320/scripts/clipboard.swf';
SyntaxHighlighter.brushes.CSharp.aliases = ['c#', 'c-sharp', 'csharp'];
SyntaxHighlighter.all();