Combine two linq expressions to inject navigation property
Asked Answered
S

2

6

I have identical set of conditions that are applied to one class directly or to some other class having same navigation property.

For example, I have home and I have agent, agent is associated with home.

So I am looking for a home with an agency name 'a', or I am looking for agent with name 'a', queries are as follow,

 Expression<<Func<Agent,bool>> ax = x=> x.Name == "a" ;

 Expression<Func<Home,bool>> hx = x=> x.Agent.Name == "a";

I have nearly 100 search queries for Agent, and I have to also apply them to Home queryable as well. I dont mind writing all again, but its difficult to maintain as we know they will change frequently during course of development.

What I want to do is, I want to compose expression for Home query like this,

 Expression<Func<Home,bool>> hx = Combine( x=>x.Agent , x=>x.Name == "a");

Where Combine will be following,

 Expression<Func<T,bool>> Combine<T,TNav>( 
     Expression<Func<T,TNav>> parent, 
     Expression<Func<TNav,bool>> nav){

     // combine above to form...


     (x=>x.Agent , x=>x.Name == "a") 
         => x => x.Agent.Name == "a"

     (x=>x.Agent, x=>x.Name.StartsWith("a") || x.Name.EndsWith("a"))
         => x=>x.Agent.Name.StartsWith("a") || x.Agent.Name.EndsWith("a")

     // look carefully, x gets replaced by x.Agent in every node..

     // I know very little about expression rewriting, so I need little help
 }
Sinuate answered 5/6, 2012 at 14:3 Comment(2)
Is that an exclusive or inclusive or that you want?Grantor
@Vijay what do you mean by exclusive or inclusive? I just want to combine expressions, I am not worried about the results, x gets replaced by x.Agent as shown in 2nd example.Sinuate
D
5

Yes, you do need a visitor to replace parts of the original expressions. You could do something like that:

Expression<Func<T,bool>> Combine<T,TNav>(Expression<Func<T,TNav>> parent, Expression<Func<TNav,bool>> nav)
{
     var param = Expression.Parameter(typeof(T), "x");
     var visitor = new ReplacementVisitor(parent.Parameters[0], param);
     var newParentBody = visitor.Visit(parent.Body);
     visitor = new ReplacementVisitor(nav.Parameters[0], newParentBody);
     var body = visitor.Visit(nav.Body);
     return Expression.Lambda<Func<T, bool>>(body, param);
}

public class ReplacementVisitor : System.Linq.Expressions.ExpressionVisitor
{
    private readonly Expression _oldExpr;
    private readonly Expression _newExpr;
    public ReplacementVisitor(Expression oldExpr, Expression newExpr)
    {
        _oldExpr = oldExpr;
        _newExpr = newExpr;
    }

    public override Expression Visit(Expression node)
    {
        if (node == _oldExpr)
            return _newExpr;
        return base.Visit(node);
    }

}

Example usage:

Expression<Func<Foo, Bar>> expr1 = f => f.Bar;
Expression<Func<Bar, bool>> expr2 = b => b.Baz;
var expr = Combine(expr1, expr2); // f => f.Bar.Baz
Demetricedemetris answered 5/6, 2012 at 14:27 Comment(1)
I think you are close, I dont have time to test right now, but I will test and get back to you, looks like it is correct solution, or it might need some small changes.Sinuate
G
0

Look at my point on PredicateBuilder, I THINK it'll help you. If not please explain what you need in further detail. Dynamic Where condition with Queryover

Grantor answered 5/6, 2012 at 14:15 Comment(4)
-1 This has nothing to do with predicate builder or and/or etc, and Invoke will not work with Entity Framework, this is about expression rewriting, where end ParemeterExpression gets replaced by Member of Expression of Parent ParemeterExpression.Sinuate
Apologies, I didn't think you specified you were using Entity Framework, and I thought that by being able combine your LINQ statements you would be able to solve your issue. I'm still not 100% on what you're asking, but I'll stop trying to help and let someone else take over since you seem to have taken offence at my attempt to help you.Grantor
PredicateBuilder won't help in that case, but the rest of LinqKit could help (especially the Expand method to expand Invokes to expressions that are understood by Entity Framework)Demetricedemetris
I linked to the LinqKit page in my other answer "for more information... blah", but It seems as though it won't solve Akash's problem.Grantor

© 2022 - 2024 — McMap. All rights reserved.