【发布时间】:2011-04-12 09:15:03
【问题描述】:
我有一组这种形式的搜索条件:
member | value | operator
--------+---------+---------
height | 10 | >
height | 2 | <
name | Carl | ==
我想查询所有符合这些条件的对象。
现在,我正在这样做:
- 为每个表达式构建一个表达式 的标准
- 使用“OR”连接每个表达式 表达
- 构建 lambda 表达式包含 串联表达式
- 将 lambda 表达式传递给 IQueryable.Where() 方法
您知道使用连续 OR 对 IQueryable 集合进行动态过滤的最简单方法吗?
奖金我们的解决方案:
基于IlyaBuiluk solution @ CodeProject
// The structure used by the new extension method
public struct SearchCriteria
{
public string Column;
public object Value;
public WhereOperation Operation;
}
// How to convert the rules structure to the search criteria structure
var searchCriterias = grid.Where.rules.Select(Rule => new SearchCriteria
{
Column = Rule.field,
Operation =
(WhereOperation)
StringEnum.Parse(
typeof (WhereOperation),
Rule.op),
Value = Rule.data
}).ToArray();
// Usage:
query = query.WhereOr(searchCriterias);
// Implementation
public static IQueryable<T> WhereOr<T>( this IQueryable<T> Query, SearchCriteria [ ] Criterias )
{
if( Criterias.Count( ) == 0 )
return Query;
LambdaExpression lambda;
Expression resultCondition = null;
// Create a member expression pointing to given column
ParameterExpression parameter = Expression.Parameter( Query.ElementType, "p" );
foreach( var searchCriteria in Criterias )
{
if( string.IsNullOrEmpty( searchCriteria.Column ) )
continue;
MemberExpression memberAccess = null;
foreach( var property in searchCriteria.Column.Split( '.' ) )
memberAccess = MemberExpression.Property
( memberAccess ?? ( parameter as Expression ), property );
// Change the type of the parameter 'value'. it is necessary for comparisons (specially for booleans)
ConstantExpression filter = Expression.Constant
(
Convert.ChangeType( searchCriteria.Value, memberAccess.Type )
);
//switch operation
Expression condition = null;
switch( searchCriteria.Operation )
{
//equal ==
case WhereOperation.Equal:
condition = Expression.Equal( memberAccess, filter );
break;
//not equal !=
case WhereOperation.NotEqual:
condition = Expression.NotEqual( memberAccess, filter );
break;
// Greater
case WhereOperation.Greater:
condition = Expression.GreaterThan( memberAccess, filter );
break;
// Greater or equal
case WhereOperation.GreaterOrEqual:
condition = Expression.GreaterThanOrEqual( memberAccess, filter );
break;
// Less
case WhereOperation.Less:
condition = Expression.LessThan( memberAccess, filter );
break;
// Less or equal
case WhereOperation.LessEqual:
condition = Expression.LessThanOrEqual( memberAccess, filter );
break;
//string.Contains()
case WhereOperation.Contains:
condition = Expression.Call( memberAccess,
typeof( string ).GetMethod( "Contains" ),
Expression.Constant( searchCriteria.Value ) );
break;
default:
continue;
}
resultCondition = resultCondition != null ? Expression.Or( resultCondition, condition ): condition;
}
lambda = Expression.Lambda( resultCondition, parameter );
MethodCallExpression result = Expression.Call(
typeof( Queryable ), "Where",
new [ ] { Query.ElementType },
Query.Expression,
lambda );
return Query.Provider.CreateQuery<T>( result );
}
【问题讨论】:
-
是的,请参阅以下行:“您知道使用连续 OR 对 IQueryable 集合进行动态过滤的最简单方法吗?”
-
@Clicktricity:很奇怪,我们必须做这一切来过滤一组符合“任何”条件的对象。另一方面,我们可以只使用连续的 IQueryable.Where() 来进行匹配“所有”条件的过滤。所以这就是问题所在,有一种简单的方法可以做到这一点吗?我的问题不够清楚。谢谢 Clicktricity :)
-
就性能和易于实施而言,这比使用动态查询库更好吗?我相信这样您可以更好地控制表达式树的 SQL 输出。
-
@Raul Roa:是的,使用 DQL 生成这些表达式树会更容易。杰出的。这种情况实际上几乎是基于字符串的查询表示的事实上的示例。非常感谢+1。您可以发表您的评论吗?
标签: c# linq linq-to-entities expression-trees iqueryable