如何组合多个表达式<func <t,bool =“”>&gt;到单个表达式执行DbContext?

时间:2017-12-08 19:37:54

标签: c# entity-framework linq linq-to-entities linqkit

我尝试创建一种方法,允许我的应用用户创建自己的数据过滤视图。为此,我编写了一个Key-Operator-Value对列表,然后我在多个步骤之后尝试解析表达式以用于Where调用。到目前为止,我可以达到创建表达式的程度,但我无法弄清楚如何将它们合并为单个表达式。

有人能指出我正确的方向吗?通过在线阅读类似的帖子,我已经决定尝试使用LINQKit PredicateBuilder来做,但我回来的只是初始的起始谓词。这是我到目前为止的代码,它绝不是最终的,我只是在LINQPad中进行原型设计:

void Main() {
    //  1. Create form post object
    var query = new QueryEdit {
        Filters = new List<Filter> {
            new Filter {
                Id = 1,
                Key = "Name",
                Operator = "=",
                Value = "New York"
            },
            new Filter {
                Id = 2,
                Key = "Name",
                Operator = "!=",
                Value = "Boston"
            },
            new Filter {
                Id = 3,
                Key = "Name",
                Operator = "=",
                Value = "Washington"
            }
        },
        FilterClause = "1 AND 3 OR 2 OR 4"
    };

    //  2. Compose filter groups
    var filterGroups = GetFilterGroups(query);

    //  3. Compose expression groups
    var expressionGroups = GetExpressionGroups<Region>(filterGroups);

    expressionGroups.Dump();

    //  4. Compose full expression
    var expression = GetExpression(expressionGroups);

    expression.Dump();

    //  5. Serialize expression to blob
    //  6. Deserialize blob to expression
    //  7. Execute expression against db
}

static IEnumerable<FilterGroup> GetFilterGroups(
    QueryEdit query) {
    //  The parentheses are meaningless, so they need to be removed.
    //  The groups are all conditions with ANDs, separated by ORs.

    return query.FilterClause.Replace(")", string.Empty).Replace("(", string.Empty).Split(new[] { "OR" }, StringSplitOptions.None).Select(
        fc => new FilterGroup {
            Filters = fc.Split(new[] { "AND" }, StringSplitOptions.None).Select(
                i => {
                    var id = Convert.ToByte(i);

                    return query.Filters.SingleOrDefault(
                        f => f.Id == id);
                }).Where(
                f => f != null).OrderBy(
                f => f.Id)
        }).Where(
        fg => fg.Filters.Any());
}

static IEnumerable<IEnumerable<Expression<Func<T, bool>>>> GetExpressionGroups<T>(
    IEnumerable<FilterGroup> filterGroups) {
    var type = typeof(T);
    var parameter = Expression.Parameter(type);

    return filterGroups.Select(
        fg => {
            return fg.Filters.Select(
                f => {
                    var left = Expression.Property(parameter, type.GetProperty(f.Key));
                    var right = Expression.Constant(f.Value);
                    var body = GetExpressionBody(f.Operator, left, right);

                    if (body == null) {
                        return null;
                    }

                    return Expression.Lambda<Func<T, bool>>(body, parameter);
                }).Where(
                e => e != null);
        });
}

static object GetExpression<T>(
    IEnumerable<IEnumerable<Expression<Func<T, bool>>>> expressionGroups) {
    var predicate = PredicateBuilder.True<T>();

    foreach (var expressionGroup in expressionGroups) {
        var expression = GetPredicateForGroup<T>(expressionGroup);

        predicate.Or(expression);
    }

    return predicate;
}

static Expression<Func<T, bool>> GetPredicateForGroup<T>(
    IEnumerable<Expression<Func<T, bool>>> expressionGroup) {
    var predicate = PredicateBuilder.True<T>();

    foreach (var expression in expressionGroup) {
        predicate.And(expression);
    }

    return predicate;
}

static Expression GetExpressionBody(
    string @operator,
    Expression left,
    Expression right) {
    switch (@operator) {
        case "=":
            return Expression.Equal(left, right);
        case "!=":
            return Expression.NotEqual(left, right);
        case ">":
            return Expression.GreaterThan(left, right);
        case ">=":
            return Expression.GreaterThanOrEqual(left, right);
        case "<":
            return Expression.LessThan(left, right);
        case "<=":
            return Expression.LessThanOrEqual(left, right);
        default:
            return null;
    }
}

sealed class QueryEdit {
    public IEnumerable<Filter> Filters { get; set; }
    public string FilterClause { get; set; }
    public int Id { get; set; }
    public string Name { get; set; }
}

sealed class Filter {
    public byte Id { get; set; }
    public string Key { get; set; }
    public string Operator { get; set; }
    public object Value { get; set; }
}

sealed class FilterGroup {
    public IEnumerable<Filter> Filters { get; set; }
}

我也对如何实现这一目标或整体改进的其他建议持开放态度。提前谢谢!

2 个答案:

答案 0 :(得分:1)

尝试

predicate = predicate.Or(expression) 

predicate = predicate.And(expression)

答案 1 :(得分:0)

您可以使用规范模式来组合谓词。我发现这非常有用 - http://gurmitteotia.blogspot.co.uk/2015/06/specification-pattern-with-repository.html#more。他的博客包括链式谓词表达式和一般模式的扩展方法,&amp;它基于EF。