Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
444 views
in Technique[技术] by (71.8m points)

c# - How can I combine two lambda expressions without using Invoke method?

I have two lambda expressions:

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

and

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

the i type, comes from my poco entities, that can't used with invoke. I want to combine these in runtime.

I want to combine these expressions in runtime in a similar way as:

Expression<Func<MyEntity, bool>> e3 = Combine(e1,e2);
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The problem is that you can't just "and"/"or" them, because you need to re-write the internals to change the parameters; if you use the .Body from e1, but the parameter from e2, it won't work - because the .Body of e1 references a completely unrelated parameter instance that isn't defined. This is more obvious if you use:

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

(note the difference between e1 using i and e2 using j)

If we combine them without rewriting the parameter, we would get the nonsensical:

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

(woah.... where did j come from?)

HOWEVER; the problem is identical regardless of the name of the parameter: it is still a different parameter.

And since the expression is immutable you can't just swap it "in place".

The trick is to use a "visitor" to rewrite the nodes, like so:

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...