Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/276.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/entity-framework/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用ExpressionVisitor创建算术公式_C#_Entity Framework_Lambda_Expression_Expressionvisitor - Fatal编程技术网

C# 使用ExpressionVisitor创建算术公式

C# 使用ExpressionVisitor创建算术公式,c#,entity-framework,lambda,expression,expressionvisitor,C#,Entity Framework,Lambda,Expression,Expressionvisitor,我试图通过实体框架lambda从模型的列创建一个动态公式 public class OutputModel { public decimal Result {get;set;} } public class TableTest { public decimal A {get;set;} public decimal B {get;set;} public decimal C {get;set;} } Expression<Func<TableTest, Outp

我试图通过实体框架lambda从模型的列创建一个动态公式

public class OutputModel
{
   public decimal Result {get;set;}
}

public class TableTest
{
  public decimal A {get;set;}
  public decimal B {get;set;}
  public decimal C {get;set;}
}

Expression<Func<TableTest, OutputModel>> _expr = t => new OutputModel();

TestExpressionVisitor _visitor = new TestExpressionVisitor();
_visitor.Visit(_expr);

var _result = new TempDataContext().TableTests.Select(_expr);
但不确定如何构造一个表达式,该表达式可以从字符串参数({a}+{B}({C}))为列执行算术函数,其中a、B和C来自TableTest,并将结果放在OutputModel.result上。我使用expressionvisitor的方法正确吗


任何帮助都将不胜感激。

要详细介绍我的评论,通常您需要:

  • 解析您的输入并将其转换为标记,其中标记只是转换为C#对象的字符串的语义组件。
    如果我们使用示例
    (a+B)*C
    ,标记将是对象“括号(打开)、变量(a)、运算符(+)、变量B、括号(关闭)、运算符(*)、变量(C)”.
    我将不详细介绍如何做到这一点,因为这不是问题的一部分,但我曾经使用正则表达式序列编写了一个非常简单但功能强大的标记器,因为它们已经完成了解析所需的大部分艰苦工作
  • 重新排序您的令牌,就像它们需要被处理一样,考虑操作符优先级规则和括号。这可以通过使用Dijkstra的调车场算法来实现,参见示例。
    您重新排序的令牌现在是“变量(A)、变量(B)、运算符(+)、变量(C)、变量(*)
  • 将令牌树或队列(无论您选择如何存储)转换为表达式树
  • 对于令牌队列,最后一步是:

      // for demo purposes I manually fill the list of tokens
      // with the tokens in order how they are output by the shunting-yard algorithm
      var tokenQueue = new Token[]
      {
        new VariableToken("A"),
        new VariableToken("B"),
        new OperatorToken("+"),
        new VariableToken("C"),
        new OperatorToken("*")
      };
      var inputParameter = Expression.Parameter(typeof(TableTest));
      var expressions = new Stack<Expression>();
      foreach (var token in tokenQueue)
      {
        // transform token to expression by using the helper methods of https://msdn.microsoft.com/de-de/library/system.linq.expressions.expression_methods(v=vs.110).aspx
        switch (token)
        {
          case VariableToken variableToken:
            // this will reference the property in your TableTest input specified by the variable name, e.g. "A" will reference TableTest.A
            expressions.Push(Expression.Property(inputParameter, variableToken.Name));
            break;
          case OperatorToken operatorToken:
            // This will take two expression from the stack, give these to input to an operator and put the result back onto the queue for use for the next operator
            var rightOperand = expressions.Pop();
            var leftOperand = expressions.Pop();
            if (operatorToken.Name == "+")
            {
              expressions.Push(Expression.Add(leftOperand, rightOperand));
            }
            else if (operatorToken.Name == "*")
            {
              expressions.Push(Expression.Multiply(leftOperand, rightOperand));
            }
            break;
        }
      }
    
      // create and fill output model with final expression
      var outputModelExpr = Expression.New(typeof(OutputModel).GetConstructor(new[] {typeof(decimal) }), expressions.Single());
    
      // create the lambda expression 
      // in this example it will have the form: x => return new OutputModel((x.A + x.B) * x.C)
      Expression<Func<TableTest, OutputModel>> lambda = Expression.Lambda<Func<TableTest, OutputModel>>(outputModelExpr, inputParameter);
    
      // only for testing purposes: compile it to a function and run it
      var calc = lambda.Compile();
      var testInput = new TableTest { A = 1, B = 2, C = 3 };
      Console.WriteLine(calc(testInput).Result); // returns 9, because (A + B) * C = (1 + 2) * 3 = 9
    
    请注意,我在OutputModel中添加了一个构造函数,因为这使表达式更简单:

    public class OutputModel
    {
       public OutputModel(decimal result) { Result = result; }
    
       public decimal Result {get;set;}
    }
    

    要进一步阐述我的意见,一般来说,您需要:

  • 解析输入并将其转换为标记,其中标记只是转换为C#对象的字符串的语义组件。
    如果我们使用示例
    (a+B)*C
    ,标记将是对象“括号(打开)、变量(a)、运算符(+)、变量B、括号(关闭)、运算符(*)、变量(C)“
    我不会详细介绍如何执行此操作,因为这不是问题的一部分,但我曾经使用正则表达式序列编写了一个非常简单但功能强大的标记器,因为它们已经完成了解析所需的大部分艰苦工作
  • 重新排序您的令牌,就像它们需要被处理一样,考虑操作符优先级规则和括号。这可以通过使用Dijkstra的调车场算法来实现,参见示例。
    您重新排序的令牌现在是“变量(A)、变量(B)、运算符(+)、变量(C)、变量(*)
  • 将令牌树或队列(无论您选择如何存储)转换为表达式树
  • 对于令牌队列,最后一步是:

      // for demo purposes I manually fill the list of tokens
      // with the tokens in order how they are output by the shunting-yard algorithm
      var tokenQueue = new Token[]
      {
        new VariableToken("A"),
        new VariableToken("B"),
        new OperatorToken("+"),
        new VariableToken("C"),
        new OperatorToken("*")
      };
      var inputParameter = Expression.Parameter(typeof(TableTest));
      var expressions = new Stack<Expression>();
      foreach (var token in tokenQueue)
      {
        // transform token to expression by using the helper methods of https://msdn.microsoft.com/de-de/library/system.linq.expressions.expression_methods(v=vs.110).aspx
        switch (token)
        {
          case VariableToken variableToken:
            // this will reference the property in your TableTest input specified by the variable name, e.g. "A" will reference TableTest.A
            expressions.Push(Expression.Property(inputParameter, variableToken.Name));
            break;
          case OperatorToken operatorToken:
            // This will take two expression from the stack, give these to input to an operator and put the result back onto the queue for use for the next operator
            var rightOperand = expressions.Pop();
            var leftOperand = expressions.Pop();
            if (operatorToken.Name == "+")
            {
              expressions.Push(Expression.Add(leftOperand, rightOperand));
            }
            else if (operatorToken.Name == "*")
            {
              expressions.Push(Expression.Multiply(leftOperand, rightOperand));
            }
            break;
        }
      }
    
      // create and fill output model with final expression
      var outputModelExpr = Expression.New(typeof(OutputModel).GetConstructor(new[] {typeof(decimal) }), expressions.Single());
    
      // create the lambda expression 
      // in this example it will have the form: x => return new OutputModel((x.A + x.B) * x.C)
      Expression<Func<TableTest, OutputModel>> lambda = Expression.Lambda<Func<TableTest, OutputModel>>(outputModelExpr, inputParameter);
    
      // only for testing purposes: compile it to a function and run it
      var calc = lambda.Compile();
      var testInput = new TableTest { A = 1, B = 2, C = 3 };
      Console.WriteLine(calc(testInput).Result); // returns 9, because (A + B) * C = (1 + 2) * 3 = 9
    
    请注意,我在OutputModel中添加了一个构造函数,因为这使表达式更简单:

    public class OutputModel
    {
       public OutputModel(decimal result) { Result = result; }
    
       public decimal Result {get;set;}
    }
    

    表达式访问者用于解析表达式。您可以使用它来创建新表达式,但可以替换和插入表达式。表达式本身可以通过使用表达式访问者来动态创建。您可以使用它来创建新表达式,但可以替换和插入表达式。表达式离子本身也可以通过使用这个sir动态创建,这非常有帮助,我很高兴现在就尝试这个。到目前为止,我设法想出了表达方法,但缺少的是Dijkstra的调车场算法。非常感谢您在此提供的详细帮助。这个sir非常有帮助,我很高兴尝试这个现在。到目前为止,我设法想出了表达方法,但缺少的是Dijkstra的调车场算法。非常感谢您在此提供的详细帮助