2012-01-09 15 views
52

ho una query LINQ che è simile al seguente:più clausole WHERE con i metodi di estensione LINQ

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

Sto cercando di imparare/capire LINQ. In alcuni casi, ho bisogno di aggiungere due ulteriori clausole WHERE. Nel tentativo di fare questo, sto usando:

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

Come potete vedere, io so come aggiungere un ulteriore clausola WHERE. Ma come aggiungo più? Per esempio, mi piacerebbe aggiungere

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

alla mia domanda precedente. Come faccio a farlo usando i metodi di estensione?

Grazie!

risposta

101

due modi:

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

io di solito preferisco la seconda. Ma vale la pena profilare il server SQL per controllare l'esecuzione della query e vedere quale si comporta meglio per i tuoi dati (se c'è qualche differenza).

Una nota sulla concatenazione dei metodi .Where(): È possibile concatenare tutti i metodi LINQ desiderati. Metodi come .Where() in realtà non vengono eseguiti contro il database (ancora). Sono defer execution fino a calcolare i risultati effettivi (ad esempio con .Count() o). Quindi, quando si concatenano più metodi (più chiamate a .Where(), forse a .OrderBy() o qualcosa del genere, ecc.) Si costruisce ciò che viene chiamato expression tree. Questo intero albero è ciò che viene eseguito contro l'origine dei dati quando arriva il momento di valutarlo.

+0

Mi sento stupido non sapendo che potrei fare questo .. Mi hai appena salvato da tanto codice spaghetti. – ledgeJumper

+0

Grazie, mi ha aiutato. Ma è anche possibile che io inneschi una qualsiasi delle clausole where in base a una certa variabile? @David –

0

Basta usare l'operatore && come si farebbe con qualsiasi altra istruzione che è necessario eseguire la logica booleana.

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

Sicuramente:

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

o solo un altro .Where() chiamata come questo (anche se non so perché si vorrebbe, se non è diviso da un altro variabile di controllo booleana):

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

Oppure un'altra riassegnazione a results: `results = results.Where (blah).

14

Puoi continuare a incatenarli come hai fatto tu.

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

Questo rappresenta un AND.

+0

Tu - e gli altri - picchia anche me, ma questo è probabilmente il modo più leggibile per farlo. –

+4

Ripetute le clausole where vengono aggiunte alla query con un operatore "and" in mezzo. – linkerro

+0

Questa probabilmente non è la soluzione 'più pulita', ma nel mio caso è l'unica che ha funzionato finora. Ho dovuto aggiungere clausole 'where' basate su selezioni nell'interfaccia utente. –

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

La selezione non è più valida poiché si sta già lavorando con un ordine.

2

è possibile utilizzare & & e scrivere tutte le condizioni nella stessa clausola where, oppure è possibile. Dove(). Dove(). Dove() ... e così via.

7

Se si lavora con dati in memoria (leggi "collezioni di POCO") si può anche impilare le vostre espressioni insieme usando PredicateBuilder in questo modo:

// 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 fonte completa di menzionato PredicateBuilder è muggito (ma si potrebbe anche controllare la original page con alcuni altri esempi):

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: ho provato questo approccio con Portable Class Library progetto e hav e di utilizzare .Compile() per farlo funzionare:

Dove (predicato .Compile());

+0

C'è una ragione per cui questo non funzionerebbe con Entity Framework LINQ? – Ciantic

Problemi correlati