2012-02-11 17 views
32

¿Cómo puedo volver a escribir esta consulta de linq en Entity on con la expresión lambda?
Quiero usar dejar palabra clave o un equivalente en mi expresión lambda.Cómo "dejar" en expresión lambda?

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

Para algunas preguntas similares como lo que se ha comentado en virtud de mi pregunta, se sugiere a

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

que calculará AveragePrice para cada artículo, mientras que en el estilo de consultas que he mencionado, vamos expresión impide calcular el promedio muchas veces

+2

posible duplicado del [Código equivalente a la palabra clave 'let' en llamadas de método de extensión LINQ encadenadas] (http://stackoverflow.com/questions/1092687/code-equivalent-to-the-let-keyword-in-chained-linq-extension-method-calls) – Eranga

+0

@Eranga: Yo esa Pregunta, Marc tenía un nombre de animal seleccionado. Longitud para cada artículo. Aquí, no quiero calcular el Promedio de todos los artículos, para cada artículo. –

+1

@Reza: el promedio se calcula solo una vez por objeto de tienda, exactamente como en su consulta ... – digEmAll

Respuesta

36

Entonces, puede usar la sintaxis del método de extensión, que implicaría una expresión lambda más de la que está usando actualmente. No hay ninguna let, sólo tiene que utilizar un lambda de varias líneas y declara una variable:

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

Tenga en cuenta que he cambiado la comparación de precios promedio, porque la suya no volvería nunca ningún resultado (más de 500 y menos de 250) .

La alternativa es

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

Error: Una expresión lambda con un cuerpo de declaración no se puede convertir a un árbol de expresiones. –

+5

Su primera oferta solo funciona en la memoria y no se pudo usar en proveedores de LINQ como EF. Una expresión lambda con un cuerpo de declaración no se puede convertir a un árbol de expresiones. –

+0

@Jay: se calcula el precio promedio de todos los artículos de acuerdo con la segunda alternativa. Quiero usar algo como * let * para dejar de repetir este cálculo. –

19

Básicamente, es necesario utilizar un anónimo Seleccione y el tipo de añadir el promedio de su objeto, seguido por el resto de su declaración.

No se ha probado pero debería tener este aspecto:

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

Atención, tenga cuidado con estas construcciones. El uso de let crea un nuevo tipo anónimo por objeto en su colección, que consume mucha memoria con grandes colecciones ...

Mire aquí para más detalles: Let in chained extension methods

+0

No consume * cualquier * memoria si esos son LINQ-to- Consultas de entidades o similares. – svick

+2

de hecho, solo para linq-to-objects es. Thx – Yoeri

+1

Esta debería ser la respuesta aceptada, ya que le permite usar su variable en diferentes "cláusulas". – alexbchr

3

Otra opción es definir este método de extensión:

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

Luego escriba su consulta como esta:

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