IQueryable:动态创建OR过滤

时间:2010-09-14 20:50:32

标签: c# linq linq-to-entities expression-trees iqueryable

我有一套这种形式的搜索标准:

 member  |  value  |  operator
 --------+---------+---------
 height  |   10    |    >
 height  |    2    |    < 
 name    |  Carl   |   ==

我想查询所有符合这些标准的对象。

现在,我是这样做的:

  • 为每个人建立一个表达式 标准
  • 使用'OR'连接每个表达式 表达
  • 建立一个lambda 表达包含 连接表达
  • 将lambda表达式传递给IQueryable&lt;&gt; .Where()方法

您是否知道使用连续OR过滤dinamycally 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&lt;T&gt;( result );

}

2 个答案:

答案 0 :(得分:3)

如果你有一组固定的运算符和一组固定的成员,那么你几乎不用直接处理表达式树就可以写出来。我们的想法是为各种代码创建简单的lambda表达式(例如Expression<Func<Entity, string>>用于读取成员的属性以及类似的运算符),然后组合它们来构建表达式树。我described the solution here。唯一的问题是在C#中不直接支持组合表达式,因此需要进行一些预处理(请参阅“可扩展的实用程序”部分)。

然后,您可以将基本功能存储在字典中,并根据用户选择的内容选择正确的功能(或它们的组合)。例如:

NorthwindDataContext db = new NorthwindDataContext();

// A query that tests whether a property 
// (specified by 'selector' matches a string value
var queryBuilder = Linq.Func
  ((Expression<Func<Customer, string>> selector, string val) =>
      from c in db.Customers.ToExpandable()
      where selector.Expand(c).IndexOf(val) != -1
      select c);

// Dictionary with supported members...
var dict = new Dictionary<string, Expression<Func<Customer, string>>> 
  { { "CompanyName", c => c.CompanyName },
    { "Country",     c => c.Country },
    { "ContactName", c => c.ContactName } };

// Ask user for a property name & value and Build the query
string field = Console.ReadLine();
string value = Console.ReadLine();
var q = queryBuilder(dict[field], value);

本文还包含一个动态组合OR或AND条件的示例。我暂时没有更新代码,所以它需要一些工作,但我相信LINQ KIT项目也包含了这个想法的一个版本。

答案 1 :(得分:-1)

在性能和易于实现方面,与使用动态查询库相比,这是一种更好的方法吗?我相信这样你可以更好地控制表达式树的SQL输出。

Raúl Roa

相关问题