Varias cláusulas WHERE con métodos de extensión LINQ


79

Tengo una consulta LINQ que se parece a lo siguiente:

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 puede ver, sé cómo agregar una cláusula WHERE adicional. Pero, ¿cómo agrego varios? Por ejemplo, me gustaría agregar

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

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

¡Gracias!

Respuestas:


151

Dos caminos:

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

Normalmente prefiero lo ú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 el encadenamiento de .Where()métodos: puede encadenar todos los métodos LINQ que desee. Métodos como .Where()no se ejecutan realmente en la base de datos (todavía). Ellos difieren de ejecución hasta que los resultados reales se calculan (como con una .Count()o una .ToList()). Entonces, a medida que encadena varios métodos (más llamadas a .Where(), tal vez uno .OrderBy()o algo en ese sentido, etc.), construyen lo que se llama un árbol de expresión . Todo este árbol es lo que se ejecuta contra la fuente de datos cuando llega el momento de evaluarla.


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

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

¿Puedes usar esto con una cláusula de selección al final?

@New_Coder: Por supuesto. La cláusula .Where () no cambia el tipo de retorno.
David

es extraño porque cuando hago esto: List <cadena> rutas = db.ClientStatement_Inventory .Where (x => (x.statementYear == yea)) .Where (x => (x.statementMonth == mon)) .Select ( c => c.statementPath) .ToList (); No funciona. pero si solo tengo 1 cláusula where, consulta mi base de datos.

24

Puedes seguir encadenándolos como lo has hecho.

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

Esto representa un AND.


Tú, y otros, también me ganaste, pero esta es probablemente la forma más legible de hacerlo.
Gato Schroedinger

5
Se repiten las cláusulas donde se agregan a la consulta con un operador "y" en el medio.
linkerro

Probablemente esta no sea la solución 'más limpia', pero en mi caso es la única que funcionó hasta ahora. Tuve que agregar cláusulas 'donde' según las selecciones en la interfaz de usuario.
DJ van Wyk

1
¿Hay alguna manera de hacer esto de modo que el "Dónde está" ed?
EK_AllDay

11

Si trabaja con datos en memoria (lea "colecciones de POCO"), también puede apilar sus expresiones juntas 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 completa de lo mencionado se PredicateBuilderencuentra a continuación (pero también puede consultar la página original con algunos ejemplos más):

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 enfoque con el proyecto de biblioteca de clases portátil y tengo que usarlo .Compile()para que funcione:

Donde (predicado .Compile () );


¿Hay alguna razón por la que esto no funcione con Entity Framework LINQ?
Ciantic

También funciona bien con EF Core para mí. El predicado resultante se traduce correctamente a SQL.
Thomas Hilbert

5

Seguramente:

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

O simplemente otra .Where()llamada como esta (aunque no sé por qué querría hacerlo, a menos que esté dividida por otra variable de control booleana):

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

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


2

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


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

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


0

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

if (useAdditionalClauses)
{
  results = results.Where(
                  o => o.OrderStatus == OrderStatus.Open 
                  && o.CustomerID == customerID)     
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.