how to build case insensitive strong typed LINQ query in c#?
I try to build extension method for IQuerable like this:
public static IQueryable<T> FilterByString<T>(this IQueryable<T> query,
Expression<Func<T, string>> propertySelector,
StringOperator operand,
string value)
that will generalize something like this:
query.Where(o => o.Name.ToLower().StartsWith(filterObject.Name.ToLower()))
into:
q.FilterByString(o => o.Name, filterObject.NameOperator, filterObject.Name)
to allow to set filtering operator (i.e. EndsWith, Contains).
I've seen on google some solutions to case sensitive filtering that requires using name of the property as a string instead of stron typed propertySelectorExpression. I've also seen solution for insensitive Contains() implementation using IndexOf() bu non of them seems to fit my needs.
For now I have this but it is not working (excpetiopn on "ToLower()" call):
static MethodInfo miTL = typeof(String).GetMethod("ToLower", System.Type.EmptyTypes);
static MethodInfo miS = typeof(String).GetMethod("StartsWith", new Type[] { typeof(String) });
static MethodInfo miC = typeof(String).GetMethod("Contains", new Type[] { typeof(String) });
static MethodInfo miE = typeof(String).GetMethod("EndsWith", new Type[] { typeof(String) });
public static IQueryable<T> FilterByString<T>(this IQueryable<T> query,
Expression<Func<T, string>> propertySelector,
StringOperator operand,
string value)
{
Expression constExp = Expression.Constant(value.ToLower());
Expression dynamicExpression = null;
switch (operand)
{
case StringOperator.StartsWith:
dynamicExpression = Expression.Call(propertySelector, miTL);
dynamicExpression = Expression.Call(dynamicExpression, miS, constExp);
break;
case StringOperator.Contains:
dynamicExpression = Expression.Call(propertySelector, miTL);
dynamicExpression = Expression.Call(dynamicExpression, miC, constExp);
break;
case StringOperator.EndsWith:
dynamicExpression = Expression.Call(dynamicExpression, miTL);
dynamic开发者_JAVA技巧Expression = Expression.Call(dynamicExpression, miE, constExp);
break;
default:
break;
}
LambdaExpression pred = Expression.Lambda(dynamicExpression);
return (IQueryable<T>)query.Provider.CreateQuery(Expression.Call(typeof(Queryable),
"Where", new Type[] {query.ElementType}, query.Expression, pred));
}
Any ideas?
This should work, even if certainly not complete (nor elegant).
public static class LinqQueries
{
private static MethodInfo miTL = typeof(String).GetMethod("ToLower", Type.EmptyTypes);
private static MethodInfo miS = typeof(String).GetMethod("StartsWith", new Type[] { typeof(String) });
private static MethodInfo miC = typeof(String).GetMethod("Contains", new Type[] { typeof(String) });
private static MethodInfo miE = typeof(String).GetMethod("EndsWith", new Type[] { typeof(String) });
public static IQueryable<T> FilterByString<T>(this IQueryable<T> query,
Expression<Func<T, string>> propertySelector,
StringOperator operand,
string value)
{
ParameterExpression parameterExpression = null;
var memberExpression = GetMemberExpression(propertySelector.Body, out parameterExpression);
var dynamicExpression = Expression.Call(memberExpression, miTL);
Expression constExp = Expression.Constant(value.ToLower());
switch (operand)
{
case StringOperator.StartsWith:
dynamicExpression = Expression.Call(dynamicExpression, miS, constExp);
break;
case StringOperator.Contains:
dynamicExpression = Expression.Call(dynamicExpression, miC, constExp);
break;
case StringOperator.EndsWith:
dynamicExpression = Expression.Call(dynamicExpression, miE, constExp);
break;
}
var pred = Expression.Lambda<Func<T, bool>>(dynamicExpression, new[] { parameterExpression });
return query.Where(pred);
}
private static Expression GetMemberExpression(Expression expression, out ParameterExpression parameterExpression)
{
parameterExpression = null;
if (expression is MemberExpression)
{
var memberExpression = expression as MemberExpression;
while (!(memberExpression.Expression is ParameterExpression))
memberExpression = memberExpression.Expression as MemberExpression;
parameterExpression = memberExpression.Expression as ParameterExpression;
return expression as MemberExpression;
}
if (expression is MethodCallExpression)
{
var methodCallExpression = expression as MethodCallExpression;
parameterExpression = methodCallExpression.Object as ParameterExpression;
return methodCallExpression;
}
return null;
}
}
Will manage
xxx.FilterByString(m => m.Name,...)
xxx.FilterByString(m => m.Test.Name,...)
xxx.FilterByString(m => m.GetValue(),...)//GetValue() returns "string"
CAUTION : NullReferenceExceptions aren't managed (if Name is null, or Test is null, or Test.Name is null, or GetValue() returns null)...
精彩评论