2010-10-19 3 views
1

Я пытаюсь создать дерево выражений, похожий на выполнение подзапросов, как (?):арифметические операции над подзапросов с использованием Expression.Subtract

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 для вызова деревьев:

Expression.Subtract ( Expression.Invoke (SumBar1, Expression.Parameter (TypeOf (бар)), Expression.Invoke (SumBar2, Expression.Constant (TypeOf (бар))));

Но я получаю:

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

Есть ли способ в сочетании двух деревьев выражений в новое дерево, вычитая их, проходя по параметру?

ответ

2

Это происходит при динамическом наращивании запросов Linq для EF, и вы почти добрались туда. Я написал код, чтобы сделать это вручную, но делать с LinqKit намного проще.

Как только вы используете LinqKit, просто напишите лямбда-выражение, которое вызывает оба подвыражения и вычитает результаты. Затем вызовите «Развернуть» в полученном выражении и сохраните результат. Новое выражение не будет использовать longe reave, потому что аргументы, переданные внутренним выражениям, были заменены на их тела и удалены вызовы метода.

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

Большое спасибо за это! –

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 >', другими словами, он будет бить DB дважды при выполнении totalSum. –

+0

Хм, справа. Теперь возвращаем эту часть в исходный код :) – Onkelborg

+1

Теперь я получаю ошибку компиляции на 'sumBar1 (bar)' и 'sumBar2':« Предполагается метод, делегат или событие ». Это связано с тем, что вы не можете вызывать 'Expression >' таким образом. Вы можете вызвать '.Compile(). Invoke (bar)' или 'Expression.Invoke (sumBar1, Expression.Constant (bar))'. Первый вызывает DB-переходы DB, последний вызывает ошибку, которую я описал в моем вопросе –

Смежные вопросы