views:

52

answers:

2

I'm trying to create an expression tree that's similar to performing subqueries like:

SELECT (SELECT Sum(Foo) FROM Bar1) - (SELECT Sum(Foo) FROM Bar2))

I'm trying to reuse 2 expression trees that are too complex to repeat.

What I have right now is 2 (simplified) expression trees:

Expression<Func<Bar, int>> SumBar1 =
    (bar) => (from b in bar.Something
              where b.Type = 1
              select b).Sum();

Expression<Func<Bar, int>> SumBar2 =
    (bar) => (from b in bar.Something
              where b.Type = 2
              select b).Sum();

I've already tried to use Expression.Subtract:

Expression foo = Expression.Subtract(SumBar1, SumBar2);

This fails with the error:

The binary operator Subtract is not defined for the types 'System.Func2[Bar,System.Int32]' and 'System.Func2[Bar,System.Int32]'.

I also tried using Expression.Invoke to call the trees:

Expression.Subtract( Expression.Invoke(SumBar1, Expression.Parameter(typeof(Bar)), Expression.Invoke(SumBar2, Expression.Constant(typeof(Bar))));

But then I get:

The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.

Is there any way to combined the two expression trees into a new tree, subtracting them, and passing along the parameter?

A: 

Now I don't know EF, but LINQ, and this sounds a bit strange to me. Why do you want to subtract a delegate from another?

More appropriate would be something like:

Expression<Func<Bar, int>> sumBar1 =
    (bar) => (from b in bar.Something
              where b.Type = 1
              select b).Sum();

Expression<Func<Bar, int>> sumBar2 =
    (bar) => (from b in bar.Something
              where b.Type = 2
              select b).Sum();

Expression<Func<Bar, int>> totalSum =
    bar =>
              sumBar1(bar) - sumBar2(bar);

totalSum(DB.GetBar());

I have to make a reservation though, I haven't really tested this, and this can be totally wrong.. :)

Onkelborg
The reason is that if you want to calculator the total sum for a large list of Bar instances, you end up hitting the database N times. To be able to fix that, you need to build an expression tree, so one big SQL query can be generated. Your code creates 2 instance of `Func<Bar, int>`, not `Expression<Func<T>>`, in other words it will hit the DB twice when executing totalSum.
Sander Rijken
Hm, your right. Reverting that part to original code now :)
Onkelborg
Now I get a compilation error on `sumBar1(bar)` and `sumBar2`: "Method, delegate or event is expected". This is because you can't invoke an `Expression<Func<>>` that way. You can either call `.Compile().Invoke(bar)` or `Expression.Invoke(sumBar1, Expression.Constant(bar))`. The former causes DB round-trips, the latter causes the error I described in my question
Sander Rijken
Hm, true.. Do you need to have the step where you store the expressions in variables? What if you write together everything? (If you need, you should probably get the content of the func-expressions I think)
Onkelborg
yeah, writing it all at once is the last resort solution. The problem there is that this bit of code, which is pretty complex in itself, is reused in several other expressions. It would both be against the DRY principle, and also cause a lot of noise (because the resulting query would be really complex)
Sander Rijken
I think you should get the content of the expression somehow, cause you don't want an expression that describes a method, you want an expression that describes the content of that method. I think :/
Onkelborg
+1  A: 

This comes up a lot when dynamically building up Linq queries for EF, and you almost got there. I've written code to do this by hand before, but doing with with LinqKit is so much easier.

Once you are using LinqKit, just write a lambda expression that invokes both subexpressions and subtracts the results. Then call "Expand" on the resulting expression and save the result. The new expression will no longe rhave the invokes because the arguments passed to the inner expressions have been substituted into their bodies and the method calls removed.

Expression<Func<Bar, int>> SumBar1 = 
    (bar) => (from b in bar.Something 
              where b.Type = 1 
              select b).Sum(); 

Expression<Func<Bar, int>> SumBar2 = 
    (bar) => (from b in bar.Something 
              where b.Type = 2 
              select b).Sum();

Expression<Func<Bar, int>> Combined = (bar) => SumBar1.Invoke(bar) - SumBar2.Invoke(bar);
Expression<Func<Bar, int>> Result = Combined.Expand();
Chris
Thanks a lot for that!
Sander Rijken