2012-01-09 17 views
52

Mam kwerendy LINQ, który wygląda tak:Wiele WHERE z metod rozszerzenie LINQ

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

Próbuję 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 rozszerzeń?

Dziękujemy!

Odpowiedz

101

dwa sposoby:

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

I zazwyczaj wolą tego ostatniego. Ale warto wyprofilować serwer SQL, aby sprawdzić wykonanie zapytania i zobaczyć, który z nich działa lepiej dla twoich danych (jeśli jest jakakolwiek różnica).

Uwaga dotycząca łączenia metod .Where(): Można łączyć ze sobą wszystkie pożądane metody LINQ. Metody takie jak .Where() w rzeczywistości nie wykonują względem bazy danych (jeszcze). One defer execution do momentu obliczenia rzeczywistych wyników (np. Z .Count() lub .ToList()). Tak więc, gdy łączysz ze sobą wiele metod (więcej wywołań do .Where(), może .OrderBy() lub coś podobnego do tego itp.), Budują to, co nazywa się expression tree. Całe to drzewo zostaje wykonane w stosunku do źródła danych, gdy nadejdzie czas na jego ocenę.

+0

Czuję się głupio, nie wiedząc, że mogę to zrobić. Właśnie uratowałeś mnie przed tak dużym zakresem spaghetti. – ledgeJumper

+0

Dzięki, pomogło mi to. Ale czy jest również możliwe, że uruchomię jedną z klauzul where w zależności od pewnej zmiennej? @ David –

0

Wystarczy użyć operatora &&, tak jak w przypadku innych instrukcji, które wymagają logiki Boole'a.

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

pewnością:

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

Albo po prostu inna .Where() wywołanie jak ten (choć nie wiem, dlaczego, co chcesz, o ile nie zostanie podzielona przez inną zmienną kontrolną Boolean):

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

Lub inna zmiana przypisania do results: `wyniki = wyniki. Gdzie (bla).

14

Możesz kontynuować ich łańcuchy, tak jak zrobiłeś.

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

To reprezentuje AND.

+0

Ty - i inni - również mnie bijcie, ale jest to prawdopodobnie najbardziej czytelny sposób na zrobienie tego. –

+4

Powtórzone, gdzie klauzule są dodawane do zapytania z operatorem "i" pomiędzy. – linkerro

+0

To prawdopodobnie nie jest najczystsze rozwiązanie, ale w moim przypadku to jedyne, które działało do tej pory. Musiałem dodać klauzule "where" na podstawie wyborów w interfejsie użytkownika. –

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

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

2

możesz użyć & & i napisać wszystkie warunki w tym samym klauzuli where, lub możesz .Where(). Where(). Where() ... i tak dalej.

7

Jeśli praca z w pamięci danych (czytaj „zbiory POCO”) można również układać swoje wyrażenia razem używając PredicateBuilder tak:

// 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łem wspomnianej PredicateBuilder jest poniżej (ale może również sprawdzić original page z kilkoma więcej przykładów):

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 Portable Class Library projektu hAV e korzystać .Compile(), aby to działało:

Gdzie (orzeczenie .Compile());

+0

Czy istnieje powód, dla którego nie byłoby to możliwe w przypadku Entity Framework LINQ? – Ciantic