【问题标题】:IQueryable: Creating dynamically an OR filteringIQueryable:动态创建 OR 过滤
【发布时间】: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&lt;T&gt;( result );

}

【问题讨论】:

  • 是的,请参阅以下行:“您知道使用连续 OR 对 IQueryable 集合进行动态过滤的最简单方法吗?”
  • @Clicktricity:很奇怪,我们必须做这一切来过滤一组符合“任何”条件的对象。另一方面,我们可以只使用连续的 IQueryable.Where() 来进行匹配“所有”条件的过滤。所以这就是问题所在,有一种简单的方法可以做到这一点吗?我的问题不够清楚。谢谢 Clicktricity :)
  • 就性能和易于实施而言,这比使用动态查询库更好吗?我相信这样您可以更好地控制表达式树的 SQL 输出。
  • @Raul Roa:是的,使用 DQL 生成这些表达式树会更容易。杰出的。这种情况实际上几乎是基于字符串的查询表示的事实上的示例。非常感谢+1。您可以发表您的评论吗?

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


【解决方案1】:

如果您有一组固定的运算符和一组固定的成员,那么您几乎可以编写此代码而无需直接处理表达式树。这个想法是为各种代码创建简单的 lambda 表达式(例如,Expression&lt;Func&lt;Entity, string&gt;&gt; 用于读取成员的属性,类似地用于运算符),然后将它们组合起来以构建表达式树。我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
  • 您博客上的精彩帖子。您以非常清晰的方式使主题变得清晰。
【解决方案2】:

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

Raúl Roa

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-03-26
    • 1970-01-01
    • 2011-02-24
    • 2015-04-26
    • 2013-09-16
    • 2015-02-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多