web-dev-qa-db-fra.com

Plusieurs clauses WHERE avec méthodes d'extension LINQ

J'ai une requête LINQ qui ressemble à ce qui suit:

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

J'essaie d'apprendre/comprendre LINQ. Dans certains cas, je dois ajouter deux clauses WHERE supplémentaires. Dans un effort pour cela, j'utilise:

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

Comme vous pouvez le constater, je sais comment ajouter une clause WHERE supplémentaire. Mais comment puis-je ajouter plusieurs? Par exemple, j'aimerais ajouter

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

à ma requête précédente. Comment puis-je faire cela en utilisant des méthodes d'extension?

Merci!

67
user609886

Deux voies:

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

ou:

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

Je préfère généralement ce dernier. Mais il est utile de profiler le serveur SQL pour vérifier l’exécution de la requête et déterminer celle qui fonctionne le mieux pour vos données (s’il existe une différence quelconque).

Remarque à propos du chaînage des méthodes .Where(): vous pouvez chaîner toutes les méthodes LINQ souhaitées. Des méthodes comme .Where() ne sont pas encore exécutées sur la base de données. Ils reportent l'exécution jusqu'à ce que les résultats réels soient calculés (comme avec un .Count() ou un .ToList()). Ainsi, lorsque vous enchaînez plusieurs méthodes (plus d'appels à .Where(), peut-être une .OrderBy() ou quelque chose de similaire, etc.), ils construisent ce qu'on appelle un arbre d'expression . Toute cette arborescence est ce qui est exécuté sur la source de données lorsque vient le temps de l’évaluer.

131
David

Vous pouvez continuer à les chaîner comme vous l'avez fait.

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

Cela représente un ET.

18
Bryan Boettcher

Si vous travaillez avec des données en mémoire (lisez "collections de POCO"), vous pouvez également empiler vos expressions ensemble en utilisant PredicateBuilder comme ceci:

// 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 source complète de PredicateBuilder mentionnée est ci-dessous (mais vous pouvez également vérifier la page d'origine avec quelques exemples supplémentaires):

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

Remarque : J'ai testé cette approche avec bibliothèque de classes portable projet et je dois utiliser .Compile() pour fais-le fonctionner:

Où (prédicat . Compile () );

7
Sevenate

Sûrement:

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

Ou simplement un autre appel .Where() comme celui-ci (bien que je ne sache pas pourquoi vous voudriez le faire, à moins qu'il ne soit divisé par une autre variable de contrôle booléen):

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

Ou une autre réaffectation à results: `résultats = résultats.Où (blah).

4
Andras Zoltan

vous pouvez utiliser && et écrire toutes les conditions dans la même clause where, ou vous pouvez .Where (). Where (). Where () ... et ainsi de suite.

2
Josh C.
results = context.Orders.Where(o => o.OrderDate <= today && today <= o.OrderDate)

La sélection est en cours puisque vous travaillez déjà avec une commande.

1
Gent

Il suffit d'utiliser le && opérateur comme vous le feriez avec n'importe quelle autre déclaration nécessitant une logique booléenne.

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