2012-02-11 1 views
32

Как я могу переписать этот запрос linq для Entity on с помощью выражения лямбда?
Я хочу использовать let ключевое слово или эквивалент в выражении лямбда.Как «позволить» в выражении лямбды?

var results = from store in Stores 
       let AveragePrice = store.Sales.Average(s => s.Price) 
       where AveragePrice < 500 && AveragePrice > 250 

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

.Select(store=> new { AveragePrice = store.Sales.Average(s => s.Price), store}) 

который рассчитает AveragePrice для каждого элемента, в то время как в стиле запросов я упомянул, пусть выражение мешает вычислить среднее число раз.

+2

возможно дубликат [код эквивалентном ключевое слово 'let' в цепочке вызовов метода расширения LINQ] (http://stackoverflow.com/questions/1092687/code-equivalent-to-the-let-keyword-in-chained-linq-extension-method-calls) – Eranga

+0

@Eranga: Я, что вопрос, Марк выбрал animalName.Length для каждого элемента. Здесь я не хочу вычислять Среднее значение всех элементов для каждого элемента. –

+1

@Reza: среднее значение вычисляется только один раз для объекта хранилища, точно так же, как в вашем запросе ... – digEmAll

ответ

36

Итак, вы можете использовать синтаксис метода расширения, который будет включать в себя одно лямбда-выражение больше, чем вы сейчас используете. Там нет let, вы просто использовать многострочный лямбда и объявить переменную:

var results = Stores.Where(store => 
{ 
    var averagePrice = store.Sales.Average(s => s.Price); 
    return averagePrice > 250 && averagePrice < 500; 
}); 

Обратите внимание, что я изменил среднее сравнение цен, потому что ваш никогда бы не дал никаких результатов (более 500 и менее 250) ,

Альтернатива

var results = Stores.Select(store => new { Store = store, AveragePrice = store.Sales.Average(s => s.Price}) 
    .Where(x => x.AveragePrice > 250 && x.AveragePrice < 500) 
    .Select(x => x.Store); 
+4

Ошибка: выражение лямбда с телом оператора не может быть преобразовано в дерево выражений. –

+5

Ваше первое предложение работает только в памяти и не может использоваться в LINQ-провайдерах, таких как EF. Лямбда-выражение с телом оператора не может быть преобразовано в дерево выражений. –

+0

@Jay: AvarageЦена всех предметов вычисляется для каждого элемента в соответствии со вторым альтернативом. Я хочу использовать что-то вроде * let *, чтобы прекратить повторять этот расчет. –

19

В принципе, вы должны использовать Select и анонимный тип, чтобы добавить среднее к объекту, а затем остальной части заявления.

Не тестировался, но он должен выглядеть следующим образом:

Stores.Select(
x => new { averagePrice = x.Sales.Average(s => s.Price), store = x}) 
.Where(y => y.averagePrice > 500 && y.averagePrice < 250) 
.Select(x => x.store); 

Внимание, будьте осторожны с этими конструкциями. Использование пусть создает новый анонимный типа для каждого объекта в вашей коллекции, он потребляет много памяти с большими коллекциями ...

Посмотрите здесь для подробностей: Let in chained extension methods

+0

Он не потребляет * никакой * памяти, если это LINQ-to- Запросы объектов или аналогичные. – svick

+2

действительно, только для linq-to-objects. Thx – Yoeri

+1

Это должен быть принятый ответ, поскольку он позволяет использовать вашу переменную в разных «предложениях». – alexbchr

3

Другого варианта заключается в определении этого метода расширения:

public static class Functional 
{ 
    public static TResult Pipe<T, TResult>(this T value, Func<T, TResult> func) 
    { 
     return func(value); 
    } 
}  

Затем напишите свой запрос, как это:

var results = Stores 
    .Where(store => store.Sales.Average(s => s.Price) 
     .Pipe(averagePrice => averagePrice < 500 && averagePrice > 250));