Filter data with multiple complex clauses joined with OR operation in where function

眉间皱痕 提交于 2020-01-07 04:50:19

问题


How is it possible to filter data based on some set of filters or expressions that should be used with or operation in where clause?

For example, there is a class:

class DTOFilter
{
    public string Domain { get; set; }
    public string Mobile { get; set; }
}

It is required to filter Users list based on the list of the filters next way:

u=>
(u.Email.Contains(filters[0].Domain) && u.PhoneNumber.StartsWith(filters[0].Mobile)) ||
(u.Email.Contains(filters[1].Domain) && u.PhoneNumber.StartsWith(filters[1].Mobile)) || 
...

Obviously that should be build automaticaly in form like:

db.Users.Where(filters.Filter<Users, DTOFilter>(
                        (u, f) => u.Email.Contains(f.Domain) 
                               && u.PhoneNumber.StartsWith(f.Mobile))
              .Or())

回答1:


To perform that task it's required to implement two functions:

  1. Or expression join function
  2. Filter data split function

Or function goes through a collection of expressions and joins them in a Or expression like (((expression1 or expression2) or expression3) or expression4)

public static Expression<Func<T, bool>> Or<T>(
    this IEnumerable<Expression<Func<T, bool>>> source)
    {
        var expressions = source.ToList();
        if (expressions.Count == 0)
            return x => false;

        var orExpression = expressions
            .Select(e => e.Body)
            .Aggregate((a, b) => Expression.OrElse(a, b));
        var parameter = expressions.First().Parameters.First();
        return Expression.Lambda<Func<T, bool>>(orExpression, parameter);
    }

This function can already be used if filters are already valid expressions.

Filter split data function, should multiply the selector expression and replace in the result expression parameter with the exact object value. Filter data population is done in the function:

public static IEnumerable<Expression<Func<T, bool>>> Filter<T, TData>(
    this IEnumerable<TData> data,
    Expression<Func<T,TData, bool>> selector)
{
    var parameter = selector.Parameters.First(p => p.Type.IsAssignableFrom(typeof(T)));

    return data.Select(item => Expression.Lambda<Func<T, bool>>(
        new DataVisitor<TData>(item).Visit(selector.Body), parameter));
}

Next visitor implementation helps to replace parameter with the exact value:

public class DataVisitor<T> : ExpressionVisitor
{
    private readonly ConstantExpression _data;

    public DataVisitor(T dataItem)
    {
        _data = Expression.Constant(dataItem);
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
            return node.Type.IsAssignableFrom(typeof(T))
                    ? _data : base.VisitParameter(node);
    }
}

The build expression is a correct lambda expression that can be parsed by the EF expression tree parser.



来源:https://stackoverflow.com/questions/46227467/filter-data-with-multiple-complex-clauses-joined-with-or-operation-in-where-func

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!