1

Given a query with a Where clause

CollectionA.Where(a => a.Prop1 == val1 && a.Prop2 == val2)

and another query with a similar Where clause but the properties are linked via the Reference.

CollectionB.Where(b => b.Reference.Prop1 == val1 && b.Reference.Prop2 == val2)

For functions this does work:

        Func<A, bool> f1 = a => a.Prop1 == val1 && a.Prop2 == val2;
        Func<B, bool> g1 = b => f1(b.Reference);

For expressions this doesn't work:

        Expression<Func<A, bool>> f2 = a => a.Prop1 == val1 && a.Prop2 == val2;
        Expression<Func<B, bool>> g2 = b => f2(b.Reference); // <-- Method name expected.

I would like to reuse the expression in my queries using a specification.

Like this:

Specification specification = new Specification(val1, val2)

CollectionA.Where(specification.ToExpression());

CollectionB.Where(specification.ToExpression(x => x.Reference));:


public class Specification 
{
    private readonly int val1;
    private readonly long val2;

    public Specification(int val1, long val2)
    {
        this.val1 = val1;
        this.val2 = val2;
    }

    public Expression<Func<A, bool>> ToExpression()
    {
        return x => x.Prop1 == val1 && x.Prop2 == val2;
    }

    public Expression<Func<B, bool>> ToExpression<B>(Expression<Func<B, A>> navigate)
    {
        // ?
    }
}

How to implemented this method?

Additionally I would like this to work on not only a binary 'and' expression but on any expression (i.e. any combination depth and type of parameters). (e.g. a => a.Prop1 == val1 && a.Prop2.Prop2a == val2a && a.Prop2.Prop2a == val2a) but basically it is just implementing the thing I try to do with function g2 above.

Servy
  • 202,030
  • 26
  • 332
  • 449
Wouter
  • 2,540
  • 19
  • 31
  • You didn't say what your error was, but I'm guessing it was _CS0149 - Method name expected_ on `f2(b.Reference)` because `f2` is an `Expression>`, right so far? (If so, you should probably edit your question to include this information.) – Wyck May 16 '20 at 13:42
  • @servy, how to add my answer if the question is closed? – Wouter May 16 '20 at 16:08
  • @Wouter Well in this case you're just reposting the answer someone else already provided, so there's no need to do anything. If it hadn't already been provided, you could post the answer to the canonical version of the question rather than the duplicate. – Servy May 16 '20 at 16:19

1 Answers1

-1

You can't directly call the other expression f2(b.Reference). And it would be futile to create an expression that compiles and invokes f2.

What you actually want to do is compose the expressions. Make a new expression that represents one expression chained to the other. The expression you're missing is actually just the argument selector that gets an A from a B like this: b => b.Reference;

Here's a handy Compose method (similar to this one) to help chain them together.

class A
{
    public int Prop1 = 1;
    public int Prop2 = 2;
}
class B
{
    public A Reference;
}

class Program
{
    static Expression<Func<A, C>> Compose<A, B, C>(
        Expression<Func<A, B>> fAB, Expression<Func<B, C>> fBC)
    {
        var arg = Expression.Parameter(typeof(A));
        return Expression.Lambda<Func<A, C>>(
            Expression.Invoke(fBC, Expression.Invoke(fAB, arg)), arg);
    }

    static void Main(string[] args)
    {
        int val1 = 1;
        int val2 = 2;

        Func<A, bool> f1 = a => a.Prop1 == val1 && a.Prop2 == val2;
        Func<B, bool> g1 = b => f1(b.Reference);

        Expression<Func<A, bool>> f2 = a => a.Prop1 == val1 && a.Prop2 == val2;
        Expression<Func<B, A>> argSelect = b => b.Reference;
        var g2 = Compose<B, A, bool>(argSelect, f2);    

        A objA = new A();
        B objB = new B() { Reference = objA };
        var g2Compiled = g2.Compile();
        Console.WriteLine(g2Compiled.Invoke(objB));

        // Demonstrate that it's connected to our local variable
        val2 = 3;
        Console.WriteLine(g2Compiled.Invoke(objB));
    }
}

Wyck
  • 10,311
  • 6
  • 39
  • 60
  • The questinon has been marked with a link to a duplicate which doesn't really answer the question (it contains a visitor and seems to complicate things) but i found a link in your answer: https://stackoverflow.com/a/2331190/4491768 i prefer this expression composition solution it looks nice and clean I will have a look at that. – Wouter May 16 '20 at 14:27