Определить количество условий в динамическом выражении LINQ

#c# #linq

#c# #linq

Вопрос:

Я решил внедрить System.Linq.Dynamic пространство имен в свой проект, где я полагаюсь на динамический вызов выражений LINQ из строки для моих базовых объектов. Это позволяет легко настраивать критерии на уровне данных.

 string expression = "x.Client == 100 amp;amp; x.Insurers.Any(it == 2 || it == 3)";
var x = new MyObject() { Client = 100, Insurers = new int[] { 1, 2 }};   
var p = Expression.Parameter(typeof(MyObject), "x");
var e = System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] { p }, null, expression);
bool result = e.Compile().DynamicInvoke(x); // True = condition met
  

Мой вопрос заключается в том, как мне динамически определять количество условий, содержащихся в каждом строковом выражении, чтобы я мог присвоить каждому выражению вес и выбрать то, которое имеет наибольший вес, когда происходит перекрытие. Regex может работать, но должно быть что-то более эффективное и практичное, например, дерево выражений.

Пример.:

 x.Client == 100 // Conditions = 1
x.Client == 100 amp;amp; x.Insurers.Any(it == 3) // Conditions = 2
x.Client == 100 amp;amp; x.Insurers.Any(it == 2 || it == 3) // Conditions = 3
  

Ответ №1:

Я не знаком с System.Linq.Dynamic библиотекой, но предполагая, что она создает обычные, строго типизированные Expression деревья, вы можете использовать ExpressionVisitor .

Этот подсчитывает количество булевых логических операций, таких как amp;amp; :

 int CountConditions(Expression expr)
{
    var visitor = new CountBinaryOpsVisitor();
    visitor.Visit(expr);
    return visitor.BinaryOperationCount   1;
}

class CountBinaryOpsVisitor : ExpressionVisitor
{
    public int BinaryOperationCount { get; private set; }

    protected override Expression VisitBinary(BinaryExpression node)
    {
        switch (node.NodeType)
        {
            case ExpressionType.And:
            case ExpressionType.AndAlso:
            case ExpressionType.Or:
            case ExpressionType.OrElse:
            case ExpressionType.ExclusiveOr:
                // Don't count bitwise integer operations, if they are even supported?
                if (node.Left.Type == typeof(bool))
                    BinaryOperationCount  ;
                break;
        }
        return base.VisitBinary(node);
    }
}
  

Альтернативным подходом было бы подсчитать количество операторов сравнения ( == , >= и т.д.), Но я думаю, что для этого потребуется более сложный код для обработки логических выражений, таких как x.BooleanProp или x.Insurers.Any() .

В настоящее время эта реализация не учитывает условные выражения ( x ? y : z ). Не уверен, как бы вы отнесли их к числу условий, особенно когда они вложены.

Комментарии:

1. Подсчет операторов сравнения должен работать так же легко, как подсчет логических операторов, просто это другой набор ExpressionType операций.

2. @NetMage Сложность, на которую я ссылался, заключается в том, что логическое выражение like x => x.BooleanProperty не генерирует никакого конкретного сравнения или выражения истинности, это просто MemberAccess или Call и т.д. Таким образом, вам нужно будет отличать логические значения от других обращений к элементам, таких как x.Foo.Bar.IntegerProperty , а затем убедитесь, что вы не считаете x.BooleanExpression == true дважды.

3. Подобные ответы @kalimag заставляют меня осознать, как много еще мне предстоит узнать о C #. Спасибо!