Linq: Bedingte Bedingungen zur where-Klausel hinzufügen


102

Ich habe eine solche Anfrage

(from u in DataContext.Users
       where u.Division == strUserDiv 
       && u.Age > 18
       && u.Height > strHeightinFeet  
       select new DTO_UserMaster
       {
         Prop1 = u.Name,
       }).ToList();

Ich möchte die verschiedenen Bedingungen wie Alter und Größe hinzufügen, je nachdem, ob diese Bedingungen für die Methode bereitgestellt wurden, mit der diese Abfrage ausgeführt wird. Alle Bedingungen umfassen die Benutzerabteilung. Wenn das Alter angegeben wurde, möchte ich das der Abfrage hinzufügen. Wenn die Höhe angegeben wurde, möchte ich dies ebenfalls hinzufügen.

Wenn dies mit SQL-Abfragen geschehen wäre, hätte ich den String Builder verwendet, um sie an die strSQL-Hauptabfrage anzuhängen. Aber hier in Linq kann ich mir nur vorstellen, eine IF-Bedingung zu verwenden, bei der ich dieselbe Abfrage dreimal schreibe, wobei jeder IF-Block eine zusätzliche Bedingung hat. Gibt es einen besseren Weg, dies zu tun?

Antworten:


182

Wenn Sie nicht aufrufen ToList()und Ihre endgültige Zuordnung zum DTO-Typ vornehmen, können WhereSie unterwegs Klauseln hinzufügen und die Ergebnisse am Ende erstellen:

var query = from u in DataContext.Users
   where u.Division == strUserDiv 
   && u.Age > 18
   && u.Height > strHeightinFeet
   select u;

if (useAge)
   query = query.Where(u => u.Age > age);

if (useHeight)
   query = query.Where(u => u.Height > strHeightinFeet);

// Build the results at the end
var results = query.Select(u => new DTO_UserMaster
   {
     Prop1 = u.Name,
   }).ToList();

Dies führt immer noch nur zu einem einzigen Aufruf der Datenbank, was genauso effizient ist wie das Schreiben der Abfrage in einem Durchgang.


1
Muss ich alle where-Bedingungen in die Anweisung "var query = .." einfügen?
user20358

4
Die folgenden Wo-Bedingungen werden als ODER oder als UND aggregiert?
Vi100

4
@ vi100 sie werden zusätzliche Filter sein, also UND
Reed Copsey

Gott sei Dank für die Einfachheit! Ich habe es so
satt,

Warum ich diesen Fehler LINQ to Entities does not recognize the method 'System.String get_Item(System.String)' method, and this method cannot be translated into a store expression.
bekomme

18

Normalerweise verwende ich Methodenverkettung, habe aber das gleiche Problem. Und hier ist die Erweiterung, die ich benutze

public static IQueryable<T> ConditionalWhere<T>(
        this IQueryable<T> source, 
        Func<bool> condition,
        Expression<Func<T, bool>> predicate)
    {
        if (condition())
        {
            return source.Where(predicate);
        }

        return source;
    }

Es hilft, Kettenbrüche zu vermeiden. Auch das gleiche ConditionalOrderByund ConditionalOrderByDescendingsind hilfreich.


Hilfreich, aber fügen Sie bitte ein Beispiel hinzu, wie es im Gebrauch aussehen würde.
Suncat2000

1
Es sollte wie folgt aussehen: var Früchte = warten auf db.Fruits .ConditionalWhere (() => Farbe! = Null, f => f.Color == Farbe) .ConditionalWhere (() => reif! = Null, f => f .Ripe == reif) .ToListAsync ();
Yuriy Granovskiy

4
Funktioniert super! Vielen Dank! Ich habe auch eine Überladung für Bedingung als einfachen booleschen Wert anstelle einer Funktion vorgenommen, um sie intuitiver zu gestalten, wenn ein Delegat unnötige Komplexität hinzufügen würde. Ich verwende diese Erweiterungsmethode jetzt ziemlich häufig und schätze Ihre Lösung sehr.
Suncat2000

17

eine Option.

bool? age = null

(from u in DataContext.Users
           where u.Division == strUserDiv 
           && (age == null || (age != null && u.Age > age.Value))
           && u.Height > strHeightinFeet  
           select new DTO_UserMaster
           {
             Prop1 = u.Name,
           }).ToList();

oder Sie können zur Methodensyntax für linq wechseln und if-Bedingungen verwenden, um Ausdrücke an die where-Klausel anzuhängen.


3

Ich verwende es einfach in meiner where-Klausel als

    public IList<ent_para> getList(ent_para para){
     db.table1.Where(w=>(para.abc!=""?w.para==para.abc:true==true) && (para.xyz!=""?w.xyz==para.xyz:true==true)).ToList();
}

3

Fügen Sie basierend auf bestimmten Bedingungen die where-Bedingung hinzu ...

from u in DataContext.Users
where u.Division == strUserDiv 
&& u.Age != null ? u.Age > 18 : 1== 1
&& u.Height != null ? u.Height > 18 : 1== 1
&& u.Height != null ? u.Height > 18 : 1== 1
 select new DTO_UserMaster
       {
         Prop1 = u.Name,
       }).ToList();

2

Hier ist mein Code, um etwas Ähnliches zu tun. Dies ist eine Methode in meiner WCF SOAP Web Service-API.

    public FruitListResponse GetFruits(string color, bool? ripe)
    {
        try
        {
            FruitContext db = new FruitContext();
            var query = db.Fruits.Select(f => f);
            if (color != null)
            {
                query = query.Where(f => f.Color == color);
            }
            if (ripe != null)
            {
                query = query.Where(f => f.Ripe == ripe);
            }
            return new FruitListResponse
            {
                Result = query.Select(f => new Fruit { Id = f.FruitId, Name = f.Name }).ToList()
            };
        }
        catch (Exception e)
        {
            return new FruitListResponse { ErrorMessage = e.Message };
        }
    }

Die Basisabfrage Select(f => f)bedeutet im Grunde ALLES, und die WhereKlauseln sind optional daran angehängt. Das Finale Selectist optional. Ich verwende, um die Datenbankzeilenobjekte in Ergebnisobjekte "Fruit" zu konvertieren.


0

Angenommen, der folgende Parameter:

Int? Age = 18;

Durch einfache Verwendung &&und ||bedingte Operatoren können wir eine andere Version haben.

(from u in DataContext.Users
where u.Division == strUserDiv 
    && (Age == null || u.Age > Age)
    && (Param1 == null || u.param1 == Param1)
    && u.Height > strHeightinFeet
select new DTO_UserMaster
{
    Prop1 = u.Name,
}).ToList();

Wie bei Param1 können Sie eine beliebige Anzahl von Parametern für die Suchbedingung hinzufügen.


0

Ich bin gerade auf die Suche nach etwas anderem gestoßen, dachte aber, ich würde die Lambda-Version einwerfen.

Zuerst würde ich eine Klasse wie diese erstellen, um Parameter an eine Datenschicht zu übergeben:

   public class SearchParameters() {
       public int? Age {get; set;}
       public string Division {get;set;}
       etc
    }

Dann in meiner Datenschicht so etwas:

public IQueryable<User> SearchUsers(SearchParameters params) 
{
    var query = Context.Users;
    if (params.Age.HasValue)
    {
         query = query.Where(u => u.Age == params.Age.Value);
    }
    if (!string.IsNullOrEmpty(params.Division)
    {
        query = query.Where(u => u.Division == params.Division);
    }
    etc
    return query;
}

Wo Sie die Abfrage materialisieren, liegt bei Ihnen. Möglicherweise besteht eine Ebene zwischen der App und den Daten, die db-spezifische Darstellungen in db-agnostisch konvertiert (möglicherweise fragen Sie mehrere Datenquellen ab). Diese Ebene kann mehrere Arten von Abfragbarkeiten aus diesen Quellen abrufen und sie beispielsweise einer gemeinsamen POCO-Darstellung zuordnen.


Hoppla, ich habe John Henckels Antwort nicht gesehen. Gleiche Idee.
Scott Peterson

0

Um die oben akzeptierte Antwort hier zu ergänzen , sollten Sie bei einer dynamischen Suche in einem Join ein neues Objekt mit beiden Tabellen (t1, t2) in der anfänglichen linq-Abfrage zurückgeben, damit Sie einzeln darauf zugreifen können, um die Bedingung zu erfüllen Suche.

var query = from t1 in _context.Table1
            join t2 in _context.Table2 on t1.Table1Id equals t2.Table1IdId
            select new { t1, t2 };

        if (!string.IsNullOrEmpty(searchProperty1))
        {
            query = query.Where(collection => collection.t1.TableColumn == searchProperty1);
        }
        if (!string.IsNullOrEmpty(searchProperty2))
        {
            query = query.Where(collection => collection.t2.TableColumn == searchProperty2);
        }
        ....etc.

Ich bekam die Antwort , die ich suchte , hier in Bezug auf die Verbindung von zwei Tabellen und Abfragen von bestimmten Spalten auf eine der beiden Tabellen

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.