【问题标题】:How compose Expression: selector + predicate?如何组成表达式:选择器+谓词?
【发布时间】:2012-01-10 09:20:12
【问题描述】:

假设我们有两个类

public class EntityA
{
    public EntityB EntityB { get; set; }
}

public class EntityB
{
    public string Name { get; set; }
    public bool IsDeleted { get; set; }
}

还有选择器和谓词的两个表达式

Expression<Func<EntityA, EntityB>> selector = c => c.EntityB;
Expression<Func<EntityB, bool>> predicate = c => c.IsDeleted && c.Name == "AAA";

我需要编写一个返回组合表达式的方法,例如

Expression<Func<TSource, bool>> Compose<TPropType>(Expression<Func<TSource, TPropType>> selector, Expression<Func<TPropType, bool>> predicator)
{
    // Expression API ???
}

在我的示例中,结果应该是

Expression<Func<EntityA, bool>> exp = Compose(selector, predicate);

什么等价于

Expression<Func<EntityA, bool>> exp = c => c.EntityB.IsDeleted && c.EntityB.Name == "AAA";

提前致谢。

【问题讨论】:

  • 最后一个c.Name == "AAA" 应该是c.EntityB.Name == "AAA",对吧?
  • 是的,你是对的。已更正。

标签: linq entity-framework lambda expression-trees


【解决方案1】:

调用这些 lambda 表达式肯定是您不想做的事情。你应该做的是重写表达式。您只需要一种将值绑定到 lambda 表达式的方法,就像您调用它们一样。为此,请重写表达式的主体,将参数替换为您要绑定的值。你可以使用这个SubstitutionVisitor 来帮助做到这一点:

public class SubstitutionVisitor : ExpressionVisitor
{
    public Expression OldExpr { get; set; }
    public Expression NewExpr { get; set; }

    public override Expression Visit(Expression node)
    {
        return (node == OldExpr) ? NewExpr : base.Visit(node);
    }
}

例如给定这些表达式:

Expression<Func<EntityA, EntityB>> selector =
    entityA => entityA.EntityB;
Expression<Func<EntityB, bool>> predicate =
    entityB => entityB.IsDeleted && entityB.Name == "AAA";

目标是有效地重写它,使它变成这样:

Expression<Func<EntityA, bool>> composed =
    entity => entity.EntityB.IsDeleted && entity.EntityB.Name == "AAA";
static Expression<Func<TSource, bool>> Compose<TSource, TProp>(
    Expression<Func<TSource, TProp>> selector,
    Expression<Func<TProp, bool>> predicate)
{
    var parameter = Expression.Parameter(typeof(TSource), "entity");
    var property = new SubstitutionVisitor
    {
        OldExpr = selector.Parameters.Single(),
        NewExpr = parameter,
    }.Visit(selector.Body);
    var body = new SubstitutionVisitor
    {
        OldExpr = predicate.Parameters.Single(),
        NewExpr = property,
    }.Visit(predicate.Body);
    return Expression.Lambda<Func<TSource, bool>>(body, parameter);
}

为了理解这里发生了什么,这里逐行解释:

  1. 为我们正在创建的新 lambda 创建一个新参数。

    entity => ...
    
  2. 给定选择器,将原始参数 entityA 的所有实例替换为 lambda 主体中的新参数 entity 以获取属性。

    entityA => entityA.EntityB
    // becomes
    entity.EntityB
    
  3. 给定谓词,将原始参数 entityB 的所有实例替换为先前从 lambda 的主体中获得的属性 entity.EntityB,以获得我们的新 lambda 的主体。

    entityB => entityB.IsDeleted && entityB.Name == "AAA"
    // becomes
    entity.EntityB.IsDeleted && entity.EntityB.Name == "AAA"
    
  4. 将所有内容放在新的 lambda 中。

    entity => entity.EntityB.IsDeleted && entity.EntityB.Name == "AAA"
    

【讨论】:

  • 另一种解决方案是使用 LinqKit。在这种情况下 Expression> output = c => predicate.Invoke(selector.Invoke(c));
  • 我不会这么说的。它仍然编译并调用表达式。如果它将参数 绑定 到 lambda 的参数,那么是的,我会认为它是一个解决方案。这本质上是一种更好的方法来做其他解决方案。我对那个库不太熟悉,但看起来你需要 Expand() 那个表达式才能得到我的解决方案的结果。
【解决方案2】:

您可以尝试以下方法:

static Expression<Func<TSource, bool>> Compose<TSource, TPropType>(
    Expression<Func<TSource, TPropType>> selector,
    Expression<Func<TPropType, bool>> predicator)
{
    ParameterExpression param = Expression.Parameter(typeof(TSource), "sourceObj");
    Expression invokedSelector = Expression.Invoke(selector, new Expression[] { param });
    Expression invokedPredicate = Expression.Invoke(predicator, new[] { invokedSelector });

    return Expression.Lambda<Func<TSource, bool>>(invokedPredicate, new[] { param });
}

使用方法如下:

static void Main()
{
    Expression<Func<EntityA, EntityB>> selector = c => c.EntityB;
    Expression<Func<EntityB, bool>> predicate = c => c.IsDeleted && c.Name == "AAA";

    Expression<Func<EntityA, bool>> exp = Compose(selector, predicate);
    System.Console.WriteLine(exp.Compile()(new EntityA()));
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-29
    • 1970-01-01
    • 1970-01-01
    • 2011-04-28
    • 2010-10-28
    • 1970-01-01
    • 2014-08-28
    相关资源
    最近更新 更多