Mehrere WHERE-Klauseln mit LINQ-Erweiterungsmethoden

79

Ich habe eine LINQ-Abfrage, die wie folgt aussieht:

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

Ich versuche LINQ zu lernen / zu verstehen. In einigen Fällen muss ich zwei zusätzliche WHERE-Klauseln hinzufügen. Um dies zu erreichen, verwende ich:

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

Wie Sie sehen können, kann ich eine zusätzliche WHERE-Klausel hinzufügen. Aber wie füge ich mehrere hinzu? Zum Beispiel möchte ich hinzufügen

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

zu meiner vorherigen Anfrage. Wie mache ich das mit Erweiterungsmethoden?

Vielen Dank!

user609886
quelle

Antworten:

150

Zwei Wege:

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

oder:

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

Normalerweise bevorzuge ich Letzteres. Es lohnt sich jedoch, den SQL Server zu profilieren, um die Ausführung der Abfrage zu überprüfen und festzustellen, welche für Ihre Daten eine bessere Leistung erbringt (sofern überhaupt ein Unterschied besteht).

Ein Hinweis zum Verketten der .Where()Methoden: Sie können alle gewünschten LINQ-Methoden miteinander verketten . Methoden wie .Where()werden (noch) nicht für die Datenbank ausgeführt. Sie verschieben die Ausführung, bis die tatsächlichen Ergebnisse berechnet sind (z. B. mit a .Count()oder a .ToList()). Wenn Sie also mehrere Methoden miteinander verketten (mehr Aufrufe .Where(), möglicherweise eine .OrderBy()oder etwas in diesem Sinne usw.), bilden sie einen sogenannten Ausdrucksbaum . Dieser gesamte Baum wird für die Datenquelle ausgeführt, wenn die Zeit für die Auswertung gekommen ist.

David
quelle
2
Ich fühle mich dumm, nicht zu wissen, dass ich das tun kann. Du hast mich gerade vor so viel Spaghetti-Code gerettet.
LedgeJumper
Danke, das hat mir geholfen. Aber ist es auch möglich, dass ich abhängig von einer bestimmten Variablen eine der where-Klauseln auslösen werde? @ David
Muhammad Ashikuzzaman
Können Sie dies mit einer select-Klausel am Ende verwenden?
@New_Coder: Natürlich. Die .Where () -Klausel ändert den Rückgabetyp nicht.
David
Es ist seltsam, denn wenn ich das mache: List <string> path = db.ClientStatement_Inventory .Where (x => (x.statementYear == yea)) .Where (x => (x.statementMonth == mon)) .Select ( c => c.statementPath) .ToList (); Es funktioniert nicht. aber wenn ich nur eine where-Klausel habe, fragt sie meine Datenbank ab.
24

Sie können sie weiter verketten, wie Sie es getan haben.

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

Dies stellt ein UND dar.

Bryan Boettcher
quelle
Sie - und andere - haben mich auch geschlagen, aber dies ist wahrscheinlich der am besten lesbare Weg, dies zu tun.
Schroedingers Cat
4
Wiederholt, wenn der Abfrage Klauseln mit einem Operator "und" dazwischen hinzugefügt werden.
Linkerro
Dies ist wahrscheinlich nicht die "sauberste" Lösung, aber in meinem Fall ist es die einzige, die bisher funktioniert hat. Ich musste 'where'-Klauseln hinzufügen, die auf Auswahlen in der Benutzeroberfläche basierten.
DJ van Wyk
1
Gibt es eine Möglichkeit, dies so zu tun, dass die Wo sind "ODER"?
EK_AllDay
11

Wenn Sie mit speicherinternen Daten arbeiten (lesen Sie "Sammlungen von POCO"), können Sie Ihre Ausdrücke auch mit PredicateBuilder wie folgt stapeln :

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

Die vollständige Quelle der Erwähnung PredicateBuilderist unten (Sie können aber auch die Originalseite mit einigen weiteren Beispielen überprüfen ):

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

Hinweis : Ich habe diesen Ansatz mit dem Projekt Portable Class Library getestet und muss ihn verwenden .Compile(), damit er funktioniert:

Where (Prädikat .Compile () );

Sevenate
quelle
Gibt es einen Grund, warum dies mit Entity Framework LINQ nicht funktioniert?
Ciantic
Funktioniert auch bei EF Core einwandfrei. Das resultierende Prädikat wird korrekt in SQL übersetzt.
Thomas Hilbert
5

Sicherlich:

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

Oder nur ein weiterer .Where()Aufruf wie dieser (obwohl ich nicht weiß, warum Sie das möchten, es sei denn, er wird durch eine andere boolesche Steuervariable aufgeteilt):

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

Oder eine andere Neuzuweisung zu results: `results = results.Where ( blah ).

Andras Zoltan
quelle
2

Sie können && verwenden und alle Bedingungen in dieselbe where-Klausel schreiben, oder Sie können .Where (). Where (). Where () ... und so weiter.

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

Die Auswahl ist nicht erforderlich, da Sie bereits mit einer Bestellung arbeiten.

Mann
quelle
0

Verwenden Sie den &&Operator einfach wie bei jeder anderen Anweisung, die Sie für die boolesche Logik benötigen.

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