wlf*_*84k 5 c# linq dynamic case-insensitive strong-typing
我尝试为IQuerable构建扩展方法,如下所示:
public static IQueryable<T> FilterByString<T>(this IQueryable<T> query,
Expression<Func<T, string>> propertySelector,
StringOperator operand,
string value)
Run Code Online (Sandbox Code Playgroud)
这将概括如下:
query.Where(o => o.Name.ToLower().StartsWith(filterObject.Name.ToLower()))
Run Code Online (Sandbox Code Playgroud)
成:
q.FilterByString(o => o.Name, filterObject.NameOperator, filterObject.Name)
Run Code Online (Sandbox Code Playgroud)
允许设置过滤运算符(即EndsWith,Contains).
我在google上看到了一些区分大小写的解决方案,需要使用属性的名称作为字符串而不是stron类型的propertySelectorExpression.我也看到使用IndexOf()的不敏感的Contains()实现的解决方案,它们似乎不符合我的需要.
现在我有这个,但它不起作用(在"ToLower()"调用上激动):
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));
}
Run Code Online (Sandbox Code Playgroud)
有任何想法吗?
这应该可行,即使肯定不完整(也不优雅)。
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;
}
}
Run Code Online (Sandbox Code Playgroud)
会管理
xxx.FilterByString(m => m.Name,...)
xxx.FilterByString(m => m.Test.Name,...)
xxx.FilterByString(m => m.GetValue(),...)//GetValue() returns "string"
Run Code Online (Sandbox Code Playgroud)
注意:NullReferenceExceptions 不受管理(如果 Name 为 null,或 Test 为 null,或 Test.Name 为 null,或 GetValue() 返回 null)...