Как объединить несколько выражений < Func < T, bool > > в одном выражении для выполнения против DbContext?

Gup3rSuR4c спросил: 14 ноября 2017 в 05:57 в: c#

Я пытаюсь создать способ, позволяющий пользователям моего приложения создавать собственные представления фильтра данных. Для этого я составляю список пар ключ-оператор-значение, которые после нескольких шагов пытаюсь разобрать в выражение для использования с вызовом 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; }
}

Я также открыт для других предложений о том, как выполнить это или общие улучшения. Заранее спасибо!

0 ответов