微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何将 C# 表达式转换为自定义嵌套类结构?

如何解决如何将 C# 表达式转换为自定义嵌套类结构?

我有自定义查询类,它将用于在如下所示的 lambda 表达式的帮助下构建查询

var query = new Query("Person").Where<Person>(p =>
(p.Name == "Maulik" && (p.Id == 2 || p.Age == 30)) || (p.Id == 2 && p.Name == "Maulik"));

通过基于此参考 link 类实现 ExpressionVisitor,我能够将查询翻译成字符串格式,如下所示

(((Name EqualTo "Maulik") AND ((Id EqualTo 2) OR (Age EqualTo 30))) OR ((Id EqualTo 2) AND (Name EqualTo 'Maulik')))

但是,我想将上述表达式转换为我的自定义嵌套类

public class Expression
{
    public List<Expression> Filters { get; } // nested expression
    public Operator Operator { get; set; } //e.g AND/OR
    public List<Condition> Conditions { get; } // One Expression can have many conditions
}

public class Condition
{
    public string Name { get; set; }
    public Operator Operator { get; set; } //e.g <,> ==,!= etc
    public object Value { get; set; }
}

其中表达式表示:(A > 5 && A < 10),条件表示最内层条件A > 5

嵌套我的意思是,一个表达式可以有子表达式,也可以有子子表达式等等,每个表达式可以有多个条件。

转换成这种结构的原因是为了处理各种sql/Nosql provider,这种类结构会为所有不同的provider创建单独的查询,所以这个类结构不能完全改变。

创建这个类结构是为了根据查询的 AND 和 OR 条件来维护括号序列,因此可以用单个表达式将相同级别的条件连接起来。

我正在寻找可以帮助将表达式转换为嵌套类结构的任何类型的通用映射。

将表达式转换为自定义嵌套类结构的最佳方法是什么?如果有任何可用的实现示例,请分享。我还没有找到任何相关的例子。

解决方法

我认为您的类没有正确反映表达式的结构。让我用 EBNF 写下此类表达式的可能语法(不要求完整性):

Expression = Term { ("AND" | "OR") Term }.
Term = "(" Expression ")" | Comparison.
Comparison = name ("=" | "!=" | "<" ...) Value.
Value = stringConstant | number.

我们看到的一件重要事情是,Term 可以是 ExpressionComparison。这意味着我们必须能够将一个或另一个插入到 ANDOR 的两侧。示例:Name = "Maulik" AND (Id = 2 OR Id = 3)。在 AND 的左边是一个比较,右边是一个表达式。

因此,我们必须使这两个事物的赋值兼容,例如,通过从相同的基类派生它们。

public abstract class Term
{
}

public Expression : Term
{
    public Term FirstTerm { get; set; }
    public List<(BooleanOperator operator,Term term)> SucceedingTerms { get; } = new();
}

public Comparison : Term
{
    public string Name { get; set; }
    public ComparisonOperator Operator { get; set; }
    public object Value { get; set; }
}

另一个重要的事实是语法是递归的。即,一个表达式包含术语,术语可以包含另一个表达式。这对于能够表示具有多级嵌套的括号表达式是必要的。

语法的这种递归性质有两个后果:

  1. 类结构必须允许递归结构。这是因为 Expression 类有一个包含 Term 的列表,而这些列表又可以是 Expression
  2. 后面显示的解析器必须是递归的。它包含间接递归调用(ParseExpression 调用 ParseTermParseTerm 调用 ParseExpression)。

请注意,我使用 ValueTulpes 作为由运算符和术语组成的列表元素。


现在,转换本身。你需要一个编译器。要么


因为语法简单,你可能敢于自己编写编译器。

将任务分为词法分析(由词法分析器完成)和语法分析(由解析器完成)。除了分析语法,解析器还创建所需的数据结构作为输出。词法分析器只返回一个符号流。例如

enum Symbol { EndOfInput,LeftPar,RightPar,AndOperator,OrOperator,Identifier,Number,StringLiteral,...}
private string _input;
private int _currentPos;
private string _identifier;
private string _stringValue;
private decimal _number;
private Symbol _symbol;

/// Does the lexical analysis.
private void GetSymbol()
{
    // Get next `_symbol` (and associated variables) from `_input` at `_currentPos` and
    // update `_currentPos`.
}

使用此基础架构,您可以创建解析器。解析器由密切反映语法结构的方法组成(如上面的 EBNF 中给出的)。

// Expression = Term { ("AND" | "OR") Term }.
void Expression ParseExpression()
{
    var expression = new Expression();
    expression.FirstTerm = ParseTerm();
    while (_symbol == Symbol.AndOperator || _symbol == Symbol.OrOperator) {
        var op = _symbol == Symbol.AndOperator
            ? BooleanOperator.And
            : BooleanOperator.Or;
        GetSymbol();
        term = ParseTerm();
        expression.SucceedingTerms.Add((op,term));
    }
    return expression;
}

// Term = "(" Expression ")" | Comparison.
void Term ParseTerm()
{
    Term term = null;
    if (Symbol == Symbol.LeftPar) {
        GetSymbol();
        term = ParseExpression();
        if (Symbol == Symbol.RightPar) {
            GetSymbol();
        } else {
            Error("\")\" expected.");
        }
    } else {
        term = ParseComparison();
    }
    return term;
}

这还不完整,但你明白了。由于此解析器的结构取决于语法产生式 (EBNF),因此在确定确切语法之前不要从它开始。

注意在下一个符号给出的语法中总是有可判定的路径。首先,我错了。我的任期是由Term = "(" Expression | Comparison ")" | Comparison.给出的。这里的问题是表达式以术语开头,而术语可以是比较。比较以名称开始。因此,ExpressionComparison 都可以以名称开头。当解析 Expression | Comparison 并且下一个符号是名称时,我们无法决定是否必须解析表达式或比较。

更新后的语法为 Term = "(" Expression ")" | Comparison.。现在,我们知道如果下一个符号是左括号,我们必须解析表达式,否则进行比较。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。