2010-10-19 179 views
1

我试图创建一个表达式树,它类似于像进行子查询:算术运算

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

我想重用2个表达式树过于复杂重复。

我现在所拥有的是2(简体)表达式树:

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.Subtract

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

这失败,出现错误:

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

我也尝试使用Expression.Invoke调用树木:

(Bar)),

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

但后来我得到:

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

有什么办法来组合这两个表达式树成一个新的树,减去他们,并沿参数传递?

回答

2

这都当为EF构建Linq查询动态建立很多时,你几乎到了那里。我已经编写了代码来手动完成此操作,但使用LinqKit要容易得多。

一旦您使用LinqKit,只需编写一个lambda表达式来调用两个子表达式并减去结果。然后在结果表达式上调用“Expand”并保存结果。新表达式不会调用调用,因为传递给内部表达式的参数已被替换到它们的正文中,并且方法调用被删除。

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(); 
+0

非常感谢! – 2010-10-22 15:27:38

0

现在我不知道EF,但LINQ,这听起来有点奇怪。你为什么想要从另一个减去一个委托?

更合适的会是这样的:

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

我要预约,虽然,我还没有真正进行了测试,这可能是完全错误的.. :)

+1

原因是,如果要计算大量Bar实例列表的总和,最终会将该数据库命中N次。为了解决这个问题,你需要构建一个表达式树,这样就可以生成一个大的SQL查询。你的代码创建2个'Func '实例',而不是'Expression >',换句话说,当执行totalSum时,它会触发DB两次。 – 2010-10-19 22:00:55

+0

嗯,你的权利。现在将该部分恢复为原始代码:) – Onkelborg 2010-10-19 22:05:36

+1

现在,我在'sumBar1(bar)'和'sumBar2'上得到编译错误:“预计会有方法,委托或事件”。这是因为你不能以这种方式调用“表达式”。您可以调用'.Compile()。调用(bar)'或'Expression.Invoke(sumBar1,Expression.Constant(bar))''。前者导致DB往返,后者导致我在我的问题 – 2010-10-19 22:41:00