Wiele klauzul WHERE z metodami rozszerzającymi LINQ

79

Mam zapytanie LINQ, które wygląda następująco:

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

Próbuję się nauczyć / zrozumieć LINQ. W niektórych przypadkach muszę dodać dwie dodatkowe klauzule WHERE. Aby to zrobić, używam:

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

Jak widać, wiem, jak dodać dodatkową klauzulę WHERE. Ale jak dodać wiele? Na przykład chciałbym dodać

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

do mojego poprzedniego zapytania. Jak to zrobić za pomocą metod rozszerzających?

Dziękuję Ci!

user609886
źródło

Odpowiedzi:

151

Dwie drogi:

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

lub:

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

Zwykle wolę to drugie. Warto jednak sprofilować serwer SQL, aby sprawdzić wykonanie zapytania i zobaczyć, który z nich działa lepiej dla twoich danych (jeśli w ogóle jest jakaś różnica).

Uwaga na temat łączenia .Where()metod w łańcuch: możesz łączyć wszystkie żądane metody LINQ. Metody takie jak w .Where()rzeczywistości nie są wykonywane w bazie danych (jeszcze). Że odroczenia wykonania aż rzeczywiste wyniki oblicza się (na przykład za pomocą .Count()lub .ToList()). Tak więc, gdy łączysz ze sobą wiele metod (więcej wywołań .Where(), może .OrderBy()coś podobnego itp.), Tworzą one coś, co nazywa się drzewem wyrażeń . To całe drzewo jest wykonywane względem źródła danych, gdy nadejdzie czas na jego ocenę.

David
źródło
2
Czuję się głupio, nie wiedząc, że mogę to zrobić ... Po prostu uratowałeś mnie od tylu kodów spaghetti.
ledgeJumper
Dzięki, to mi pomogło. Ale czy jest również możliwe, że wyzwolę którąkolwiek z klauzul Where w zależności od określonej zmiennej? @David
Muhammad Ashikuzzaman
czy możesz tego użyć z klauzulą ​​select na końcu?
@New_Coder: Oczywiście. Klauzula .Where () nie zmienia zwracanego typu.
David
to dziwne, ponieważ kiedy robię to: List <string> path = db.ClientStatement_Inventory .Where (x => (x.statementYear == yea)) .Where (x => (x.statementMonth == mon)) .Select ( c => c.statementPath) .ToList (); To nie działa. ale jeśli mam tylko 1 klauzulę where, to odpytuje moją bazę danych.
24

Możesz nadal łączyć je w łańcuch, tak jak zrobiłeś.

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

To reprezentuje AND.

Bryan Boettcher
źródło
Ty - i inni - też mnie pokonaliście, ale jest to prawdopodobnie najbardziej czytelny sposób, aby to zrobić.
Schroedingers Cat,
5
Powtarzane, gdy do zapytania dodano klauzule z operatorem „i” pomiędzy nimi.
linkerro
To chyba nie jest „najczystsze” rozwiązanie, ale w moim przypadku to jedyne, które do tej pory działało. Musiałem dodać klauzule „gdzie” na podstawie wyborów w interfejsie użytkownika.
DJ van Wyk,
1
Czy istnieje sposób na zrobienie tego, aby pole Gdzie są „LUB”?
EK_AllDay
11

Jeśli pracujesz z danymi w pamięci (czytaj „kolekcje POCO”), możesz również łączyć swoje wyrażenia razem za pomocą PredicateBuilder w następujący sposób:

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

Pełne źródło wspomnianych źródeł PredicateBuilderznajduje się poniżej (ale możesz też sprawdzić oryginalną stronę z kilkoma innymi przykładami):

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

Uwaga : przetestowałem to podejście z projektem Portable Class Library i muszę go użyć, .Compile()aby działało:

Gdzie (predykat .Compile () );

Siedem
źródło
Czy istnieje powód, dla którego nie działałoby to z Entity Framework LINQ?
Ciantic,
U mnie też działa dobrze z EF Core. Wynikowy predykat jest poprawnie tłumaczony na SQL.
Thomas Hilbert
5

Pewno:

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

Lub po prostu inne .Where()wywołanie, takie jak to (chociaż nie wiem, dlaczego miałbyś chcieć, chyba że jest podzielone przez inną boolowską zmienną kontrolną):

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

Lub ponowne przypisanie do results: `wyniki = wyniki. Gdzie ( bla ).

Andras Zoltan
źródło
2

możesz użyć && i zapisać wszystkie warunki w tej samej klauzuli where lub możesz .Where (). Where (). Where () ... i tak dalej.

Josh C.
źródło
1
results = context.Orders.Where(o => o.OrderDate <= today && today <= o.OrderDate)

Wybór jest zbędny, ponieważ już pracujesz nad zamówieniem.

Pan
źródło
0

Po prostu użyj &&operatora tak, jak w przypadku każdego innego stwierdzenia, które wymaga logiki boolowskiej.

if (useAdditionalClauses)
{
  results = results.Where(
                  o => o.OrderStatus == OrderStatus.Open 
                  && o.CustomerID == customerID)     
}
cadrell0
źródło