c# - IQueryable:动态创建 OR 过滤

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

我有一组这种形式的搜索条件:

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

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

现在,我正在通过:

  • 为每一个建立一个表达式 的标准
  • 使用“或”连接每个表达式 表达
  • 构建一个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 );

}

最佳答案

如果您有一组固定的运算符和一组固定的成员,那么您几乎可以在不直接处理表达式树的情况下编写它。这个想法是为各种代码片段创建简单的 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项目也包含这个想法的一个版本。

关于c# - IQueryable:动态创建 OR 过滤,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3712803/

相关文章:

c# - 如何将字节数组转换为任何类型

c# - LINQ - 将存储为字符串的数字相加

linq-to-entities - LINQ 选择一列 在另一列上

entity-framework - 将 ESQL 转换为 LINQ 到实体。按相关实体排序

c# - Linq to Entities 日期部分优化

c# - 在运行时将图像拖到 RichTextBox 内的 FlowDocument 中

c# - 如何在混合应用程序中获取有关缓冲区溢出异常的信息?

c# - 统一 Autowiring : to use or not to use?

c# - SequenceEqual linq 组

c# 对列表进行分组并获取每组的计数