如何在不使用Invoke方法的情况下合并两个Lambda表达式?

23

我有两个lambda表达式:

Expression<Func<MyEntity, bool>> e1 = i = >i.FName.Contain("john");

Expression<Func<MyEntity, bool>> e2 = i => i.LName.Contain("smith");

i类型来自我的Poco实体,不能与invoke一起使用。我想在运行时组合这些。

我希望以类似的方式在运行时组合这些表达式:

Expression<Func<MyEntity, bool>> e3 = Combine(e1,e2);

是的,和并且。我想在运行时将它们组合起来。 - PickleRick
2个回答

64
问题在于你不能简单地将它们“与”、“或”起来,因为你需要重新编写内部代码以更改参数。如果你使用e1.Body,但是来自e2参数,那么它将不起作用——因为e1.Body引用了一个完全无关的参数实例,该实例未被定义。如果你使用以下代码,则更为明显
Expression<Func<MyEntity, bool>> e1 = i => i.FName.Contains("john");
Expression<Func<MyEntity, bool>> e2 = j => j.LName.Contains("smith");

(注意e1使用ie2使用j的区别)

如果我们在不重写参数的情况下将它们组合在一起,我们会得到无意义的结果:

Expression<Func<MyEntity, bool>> combined =
         i => i.FName.Contains("john") && j.LName.Contains("smith");

(哇啊……j是从哪里来的?)

然而,问题与参数的名称无关:它仍然是不同的参数。

由于表达式是不可变的,你无法在原地交换它。

诀窍是使用“访问者”来重写节点,像这样:

using System;
using System.Linq.Expressions;

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);
    }
}

static class Program
{
    static void Main()
    {
        Expression<Func<MyEntity, bool>> e1 = i => i.FName.Contains("john");
        Expression<Func<MyEntity, bool>> e2 = i => i.LName.Contains("smith");

        // rewrite e1, using the parameter from e2; "&&"
        var lambda1 = Expression.Lambda<Func<MyEntity, bool>>(Expression.AndAlso(
            new SwapVisitor(e1.Parameters[0], e2.Parameters[0]).Visit(e1.Body),
            e2.Body), e2.Parameters);

        // rewrite e1, using the parameter from e2; "||"
        var lambda2 = Expression.Lambda<Func<MyEntity, bool>>(Expression.OrElse(
            new SwapVisitor(e1.Parameters[0], e2.Parameters[0]).Visit(e1.Body),
            e2.Body), e2.Parameters);
    }
}

太棒了!我花了两天时间尝试实现这个功能,其他答案都包含了大量的代码。非常感谢你,Mark! - KorsaR
哇!太棒了! - Tomas Chabada

-3

也许可以用一个简单的if语句吗?

using (MyDataBaseEntities db = new MyDataBaseEntities())
{
if (db.People.Any(p => p.FirstName == FirstNameText.Text && p.LastName == LastNameText.Text))
    {
        //Do something
    }
}    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接