代码之家  ›  专栏  ›  技术社区  ›  SDReyes

IQueryable:动态创建或筛选

  •  3
  • SDReyes  · 技术社区  · 15 年前

    我有一套搜索标准,格式如下:

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

    我想查询所有符合这些条件的对象。

    • 为每一个构建表达式 标准的
    • 表达
    • 包含 串联表达式

    你知道一个最简单的方法来过滤一个普通的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 回复  |  直到 15 年前
        1
  •  3
  •   Michiel van Oosterhout    6 年前

    如果您有一组固定的运算符和一组固定的成员,那么您几乎可以不用直接处理表达式树来编写它。其思想是为不同的代码段创建简单的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);
    

    LINQ KIT 这个项目也包含了这个想法的一个版本。

        2
  •  -1
  •   Community CDub    8 年前

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

    Raúl Roa