22

假设我有类似的东西

Expression<Func<SomeType, DateTime>> left = x => x.SomeDateProperty;
Expression<Func<SomeType, DateTime>> right = x => dateTimeConstant;
var binaryExpression = Expression.GreaterThan(left, right);
Expression<Func<SomeType, bool>> predicate = 
                          x => x.SomeDateProperty> dateTimeConstant;

1)如何用使用 the 的东西替换最后一行赋值的右手binaryExpressionvar predicate = x => binaryExpression;不起作用。

2) 的right总是一个常数,不一定是 DateTime.Now。会不会是更简单的Expression类型?例如,它不依赖于 SomeType,它只是一个常量。

3)如果我有GreaterThanas a string,有没有办法从这个字符串到同名的方法Expression?一般来说,如果比较方法的名称是 a string,我怎么能从字符串到实际调用同名的方法呢?Expression类上具有相同名称的方法?

如果重要,它必须与 LINQ to Entities 一起使用。

4

2 回答 2

19

1 和 2:您需要手动构建表达式树来执行此操作,编译器无法提供帮助,因为它只构建表示完整函数的现成表达式。当您想逐个构建函数时,这没有用。

这是构建所需表达式的一种简单方法:

var argument = Expression.Parameter(typeof(SomeType));
var left = Expression.Property(argument, "SomeDateProperty");
var right = Expression.Constant(DateTime.Now);

var predicate = Expression.Lambda<Func<SomeType, bool>>(
    Expression.GreaterThan(left, right),
    new[] { argument }
);

您可以将其用于试驾

var param = new SomeType { 
    SomeDateProperty = DateTime.Now.Add(TimeSpan.FromHours(-1))
};

Console.WriteLine(predicate.Compile()(param)); // "False"

3:由于您的二元谓词的可能选择数量很可能非常少,您可以使用字典来执行此操作:

var wordToExpression = 
    new Dictionary<string, Func<Expression, Expression, BinaryExpression>>
{
    { "GreaterThan", Expression.GreaterThan },
    // etc
};

然后,不是Expression.GreaterThan在第一个代码段中硬编码,而是执行类似wordToExpression["GreaterThan"](left, right).

当然,这也可以通过反射的标准方式完成。

于 2013-05-29T08:01:35.887 回答
8

使用 时GreaterThan,需要指定表达式主体,而不是 lambda 本身。不幸的是,有一个复杂的情况:x两个表达式中的 不一样

在这种特殊情况下,您几乎可以侥幸逃脱,因为第二个表达式不使用x

所以; 您的“1”和“2”应通过以下方式回答:

var binaryExpression = Expression.GreaterThan(left.Body, right.Body);
    var lambda = Expression.Lambda<Func<SomeType, bool>>(binaryExpression,
        left.Parameters);

但是,要在一般情况下处理此问题,您必须重写表达式,以修复参数:

var binaryExpression = Expression.GreaterThan(left.Body,
    new SwapVisitor(right.Parameters[0], left.Parameters[0]).Visit(right.Body));
var lambda = Expression.Lambda<Func<SomeType, bool>>(binaryExpression,
    left.Parameters);

和:

public class SwapVisitor : ExpressionVisitor
{
    private readonly Expression from, to;
    public SwapVisitor(Expression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node)
    {
        return node == from ? to : base.Visit(node);
    }
}

为你的“3”;没有内置的东西;不过,您可以使用反射:

string method = "GreaterThan";

var op = typeof(Expression).GetMethod(method,
    BindingFlags.Public | BindingFlags.Static,
    null, new[] { typeof(Expression), typeof(Expression) }, null);

var rightBody = new SwapVisitor(right.Parameters[0],
     left.Parameters[0]).Visit(right.Body);
var exp = (Expression)op.Invoke(null, new object[] { left.Body, rightBody });

var lambda = Expression.Lambda<Func<SomeType, bool>>(exp, left.Parameters);
于 2013-05-29T08:04:10.883 回答