2012-01-09 24 views
52

que tienen una consulta LINQ que se parece a lo siguiente:múltiples cláusulas WHERE con los métodos de extensión de LINQ

DateTime today = DateTime.UtcNow; 
var results = from order in context.Orders 
       where ((order.OrderDate <= today) && (today <= order.OrderDate)) 
       select order; 

Estoy tratando de aprender/entender LINQ. En algunos casos, necesito agregar dos cláusulas WHERE adicionales. En un esfuerzo por hacer esto, estoy usando:

if (useAdditionalClauses) 
{ 
    results = results.Where(o => o.OrderStatus == OrderStatus.Open) // Now I'm stuck. 
} 

Como se puede ver, sé cómo agregar un adicional cláusula WHERE. ¿Pero cómo agrego múltiples? Por ejemplo, me gustaría añadir

WHERE o.OrderStatus == OrderStatus.Open AND o.CustomerID == customerID

a mi consulta anterior. ¿Cómo hago esto usando métodos de extensión?

¡Gracias!

Respuesta

101

dos maneras:

results = results.Where(o => (o.OrderStatus == OrderStatus.Open) && 
          (o.CustomerID == customerID)); 

o:

results = results.Where(o => (o.OrderStatus == OrderStatus.Open)) 
       .Where(o => (o.CustomerID == customerID)); 

Por lo general prefieren este último. Pero vale la pena perfilar el servidor SQL para verificar la ejecución de la consulta y ver cuál funciona mejor para sus datos (si hay alguna diferencia).

Una nota sobre cómo encadenar los métodos .Where(): puede encadenar todos los métodos LINQ que desee. Los métodos como .Where() en realidad no se ejecutan contra la base de datos (todavía). Ellos defer execution hasta que se calculan los resultados reales (como con un .Count() o un .ToList()). Entonces, cuando encadena varios métodos (más llamadas a .Where(), tal vez un .OrderBy() o algo por el estilo, etc.) construyen lo que se llama expression tree. Este árbol completo es lo que se ejecuta contra el origen de datos cuando llega el momento de evaluarlo.

+0

Me siento tonto sin saber que podría hacer esto ... Me acabas de salvar de tanto código de espagueti. – ledgeJumper

+0

Gracias, eso me ayudó. ¿Pero también es posible que active una de las cláusulas where dependiendo de una determinada variable? @David –

0

Simplemente use el operador && como lo haría con cualquier otra declaración que necesite para hacer una lógica booleana.

if (useAdditionalClauses) 
{ 
    results = results.Where(
        o => o.OrderStatus == OrderStatus.Open 
        && o.CustomerID == customerID)  
} 
3

duda:

if (useAdditionalClauses) 
{ 
    results = 
    results.Where(o => o.OrderStatus == OrderStatus.Open && 
    o.CustomerID == customerID) 
} 

o simplemente otra .Where() llamada como éste (aunque no sé por qué usted quiere que, a menos que se divide por otra variable de control booleano):

if (useAdditionalClauses) 
{ 
    results = results.Where(o => o.OrderStatus == OrderStatus.Open). 
    Where(o => o.CustomerID == customerID); 
} 

u otra reasignación a results: `results = results.Where (blah).

14

Puede continuar encadenándolos como lo ha hecho.

results = results.Where (o => o.OrderStatus == OrderStatus.Open); 
results = results.Where (o => o.InvoicePaid); 

Esto representa una AND.

+0

Usted, y otros, también me ganaron, pero esta es probablemente la forma más fácil de hacerlo. –

+4

Se repitió donde las cláusulas se agregan a la consulta con un operador "and" en el medio. – linkerro

+0

Probablemente esta no sea la solución más "limpia", pero en mi caso es la única que funcionó hasta el momento. Tuve que agregar cláusulas 'where' basadas en selecciones en la interfaz de usuario. –

1
results = context.Orders.Where(o => o.OrderDate <= today && today <= o.OrderDate) 

La selección no es necesaria ya que usted está trabajando con un pedido.

2

puede usar & & y escribir todas las condiciones en la misma cláusula where, o puede .Where(). Where(). Where() ... y así sucesivamente.

7

Si se trabaja con los datos en memoria (leen "colecciones de poco") también puede apilar sus expresiones juntos usando PredicateBuilder así:

// initial "false" condition just to start "OR" clause with 
var predicate = PredicateBuilder.False<YourDataClass>(); 

if (condition1) 
{ 
    predicate = predicate.Or(d => d.SomeStringProperty == "Tom"); 
} 

if (condition2) 
{ 
    predicate = predicate.Or(d => d.SomeStringProperty == "Alex"); 
} 

if (condition3) 
{ 
    predicate = predicate.And(d => d.SomeIntProperty >= 4); 
} 

return originalCollection.Where<YourDataClass>(predicate.Compile()); 

La fuente completo del mencionado PredicateBuilder es abajo (pero también podría comprobar la original page con unos pocos más ejemplos):

using System; 
using System.Linq; 
using System.Linq.Expressions; 
using System.Collections.Generic; 

public static class PredicateBuilder 
{ 
    public static Expression<Func<T, bool>> True<T>() { return f => true; } 
    public static Expression<Func<T, bool>> False<T>() { return f => false; } 

    public static Expression<Func<T, bool>> Or<T> (this Expression<Func<T, bool>> expr1, 
                 Expression<Func<T, bool>> expr2) 
    { 
    var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression>()); 
    return Expression.Lambda<Func<T, bool>> 
      (Expression.OrElse (expr1.Body, invokedExpr), expr1.Parameters); 
    } 

    public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr1, 
                 Expression<Func<T, bool>> expr2) 
    { 
    var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression>()); 
    return Expression.Lambda<Func<T, bool>> 
      (Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters); 
    } 
} 

Nota: he probado este método con Portable Class Library proyecto y el VHA e para usar .Compile() para hacer que funcione:

Dónde (predicado .Compile());

+0

¿Hay algún motivo por el que esto no funcione con Entity Framework LINQ? – Ciantic

Cuestiones relacionadas