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);
dynamicExpression = 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)...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With