[英]Creating an expression that can be calculated at run time
我有一个产品列表,需要针对这些产品创建表达式树,这些树可以持久保存,以后再检索和执行。 这是用于客户端计算生成器的。
我是Expressions的新手,尽管我已经阅读了大量的文档,但是这里的学习曲线有些陡峭。 我想要的是能够开始累积PropertyExpression和Operand对。 这是我到目前为止的内容,不确定是否正确构造了它。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace ConsoleApplication1
{
public enum ProductType { Perishable, Fixed, Miscellaneous }
public enum OperandType { Addition, Sunbtraction, Multiplication, Division }
public class Product
{
public string Name { get; set; }
public ProductType Type { get; set; }
public float Price { get; set; }
}
public class Configuration
{
public Dictionary<string, float> Dictionary { get; set; }
}
public class Operand
{
public OperandType Type { get; set; }
}
public class CalculationPair<TEntityType, TProperty>
where TEntityType: class
where TProperty: struct
{
public ValueTypeProperty<TEntityType, TProperty> Left { get; set; }
public Operand Operand { get; set; }
public ValueTypeProperty<TEntityType, TProperty> Right { get; set; }
// How to specify TResult as an [out] parameter?
public TResult Calculate<TResult> ()
where TResult: struct
{
TResult result = default(TResult);
if (this.Operand.Type == OperandType.Multiplication)
{
// How to execute the expression?
//result = this.Left * this.Right;
}
return (result);
}
}
public class ValueTypeProperty<TEntityType, TProperty>
where TEntityType: class
where TProperty: struct
{
public string Name { get; set; }
public Expression<Func<TEntityType, TProperty>> PropertyExpression { get; set; }
}
public class ProductPriceProperty:
ValueTypeProperty<Product, float>
{
}
public static class Program
{
public static void Main ()
{
Configuration config = new Configuration();
List<Product> products = new List<Product>();
config.Dictionary.Add("ExportFactor", 80);
config.Dictionary.Add("ChannelMargin", 100);
products.Add(new Product() { Name = "1", Type = ProductType.Fixed, Price = 10 });
products.Add(new Product() { Name = "2", Type = ProductType.Miscellaneous, Price = 20 });
products.Add(new Product() { Name = "3", Type = ProductType.Perishable, Price = 30 });
foreach (var product in products)
{
if (product.Type == ProductType.Fixed)
{
CalculationPair<Product, float> calculation = new CalculationPair<Product, float>()
{
Left = new ProductPriceProperty() { Name = "Product Price", PropertyExpression = (entity => entity.Price) },
Operand = new Operand() { Type = OperandType.Multiplication },
Right = new ProductPriceProperty() { Name = "ExportFactor", PropertyExpression = (entity => config.Dictionary ["ExportFactor"]) },
};
// Calculation needs to be persisted to be retrieved later.
// ???!
// Once calculation has been reconstruction from the persistence layer, it needs to be executed.
product.Price = calculation.Calculate<float>();
}
}
}
}
}
更新 :这是我在努力的优先顺序:
CalculationPair.Calculate<TReult>()
函数中执行表达式? CalculationPair.Calculate<TReult>()
函数CalculationPair.Calculate<TReult>()
TResult指定为[out]参数? 正如乔恩所说,您可以使用常规表达式tress,也可以使闭包匿名方法像下面的代码所示:
public class CalculationPair<TEntityType, TProperty>
where TEntityType : class
where TProperty : struct
{
// not sure that first three properties are needed here
public ValueTypeProperty<TEntityType, TProperty> Left { get; set; }
public Operand Operand { get; set; }
public ValueTypeProperty<TEntityType, TProperty> Right { get; set; }
// closure method
public Func<TEntityType, TProperty> Calculator { get; set; }
}
这是使用它的Main方法的一部分:
foreach (var product in products)
{
if (product.Type == ProductType.Fixed)
{
CalculationPair<Product, float> calculation = new CalculationPair<Product, float>()
{
Left = new ProductPriceProperty() { Name = "Product Price", PropertyExpression = (entity => entity.Price) },
Operand = new Operand() { Type = OperandType.Multiplication },
Right = new ProductPriceProperty() { Name = "ExportFactor", PropertyExpression = (entity => config.Dictionary["ExportFactor"]) },
// only this property is needed, and it will handle reference to config object in the closure
Calculator = (entity) => entity.Price * config.Dictionary["ExportFactor"]
};
// Once calculation has been reconstruction from the persistence layer, it needs to be executed.
product.Price = calculation.Calculator(product);
}
}
在该示例中,没有表达式树,只有常规的关闭方法。
UPDATE1
您的Left和Right节点表达式的问题在于,每个此表达式都链接到自己的实体参数,而不是我们创建的ParameterExpression,并且指向实际实体对象,因此我们需要使用ExpressionVisitor将旧表达式重写为新表达式 。 它用于解析和重写需求。
这是重写器的代码:
public class ParameterRewriter : ExpressionVisitor
{
private readonly ParameterExpression _expToRewrite;
public ParameterRewriter(ParameterExpression expToRewrite)
{
this._expToRewrite = expToRewrite;
}
protected override Expression VisitParameter(ParameterExpression node)
{
// we just use type checking to understand that it's our parameter, and we replace it with new one
if (node.Type == this._expToRewrite.Type) return this._expToRewrite;
return base.VisitParameter(node);
}
}
这是CalculationPair类:
public class CalculationPair<TEntityType, TProperty>
where TEntityType : class
where TProperty : struct
{
public ValueTypeProperty<TEntityType, TProperty> Left { get; set; }
public Operand Operand { get; set; }
public ValueTypeProperty<TEntityType, TProperty> Right { get; set; }
public TResult Calculate<TResult>(TEntityType entity)
where TResult : struct
{
TResult result = default(TResult);
var prop = Expression.Parameter(typeof(TEntityType), "param");
var visitor = new ParameterRewriter(prop);
var leftExp = visitor.Visit(Left.PropertyExpression.Body);
var rightExp = visitor.Visit(Right.PropertyExpression.Body);
Expression body;
switch (this.Operand.Type)
{
case OperandType.Multiplication:
body = Expression.Multiply(leftExp, rightExp);
break;
case OperandType.Addition:
body = Expression.Add(leftExp, rightExp);
break;
case OperandType.Division:
body = Expression.Divide(leftExp, rightExp);
break;
case OperandType.Sunbtraction:
body = Expression.Subtract(leftExp, rightExp);
break;
default:
throw new Exception("Unknown operand type");
}
var lambda = Expression.Lambda<Func<TEntityType, TResult>>(body, prop);
// compilation is long operation, so you might need to store this Func as property and don't compile it each time
var func = lambda.Compile();
result = func(entity);
return (result);
}
}
和用法是一样的
product.Price = calculation.Calculate<float>(product);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.