Linq: aggiunta di condizioni alla clausola where in modo condizionale


103

Ho una domanda come questa

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

Voglio aggiungere le varie condizioni come età, altezza in base al fatto che tali condizioni siano state fornite al metodo che esegue questa query. Tutte le condizioni includeranno la Divisione utente. Se è stata fornita l'età, voglio aggiungerla alla query. Allo stesso modo, se è stata fornita l'altezza, voglio aggiungere anche quella.

Se questo dovesse essere fatto usando query sql, avrei usato il generatore di stringhe per aggiungerle alla query strSQL principale. Ma qui a Linq posso solo pensare di utilizzare una condizione IF in cui scriverò la stessa query tre volte, con ogni blocco IF con una condizione aggiuntiva. C'è un modo migliore per farlo?

Risposte:


182

Se non chiami ToList()e la mappatura finale al tipo DTO, puoi aggiungere Whereclausole man mano che procedi e creare i risultati alla fine:

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

Ciò risulterà comunque solo in una singola chiamata al database, che sarà effettivamente altrettanto efficiente quanto scrivere la query in un passaggio.


1
Devo inserire tutte le condizioni where nell'istruzione "var query = .."?
user20358

4
Le successive condizioni Dove vengono aggregate come OR o come AND?
Vi100

4
@ vi100 saranno filtri aggiuntivi, quindi AND
Reed Copsey

Grazie al cielo per la semplicità! Sono così stanco di vedere più di 20 righe di query Linq quando quanto sopra è molto più leggibile
justanotherdev

Perché 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.
ricevo

19

Di solito utilizzo il concatenamento di metodi ma ho lo stesso problema. Ed ecco l'estensione che uso

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

Aiuta a evitare rotture della catena. Anche lo stesso ConditionalOrderBye ConditionalOrderByDescendingsono utili.


Utile, ma potresti aggiungere un esempio di come apparirebbe in uso.
Suncat2000

1
Dovrebbe essere come: var fruits = await db.Fruits .ConditionalWhere (() => color! = Null, f => f.Color == color) .ConditionalWhere (() => ripe! = Null, f => f .Ripe == ripe) .ToListAsync ();
Yuriy Granovskiy

4
Funziona alla grande! Grazie! Ho anche creato un overload per condition come un semplice valore booleano invece di una funzione, per renderlo più intuitivo dove un delegato aggiungerebbe complessità non necessaria. Uso questo metodo di estensione abbastanza frequentemente ora e apprezzo molto la tua soluzione.
Suncat2000

17

un'opzione.

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

oppure è possibile passare alla sintassi del metodo per linq e utilizzare le condizioni if ​​per allegare espressioni alla clausola where.


3

Semplicemente lo sto usando nella mia clausola where come

    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

In base a determinate condizioni, aggiungere la condizione where ...

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

Ecco il mio codice per fare una cosa simile. Questo è un metodo sulla mia API del servizio Web SOAP WCF.

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

La query di base è Select(f => f)che significa fondamentalmente TUTTO e le Whereclausole sono opzionalmente allegate ad essa. La finale Selectè facoltativa. Uso per convertire gli oggetti delle righe del database in oggetti "Fruit" dei risultati.


0

Supponendo il seguente parametro,

Int? Age = 18;

Usando semplicemente &&e ||operatori condizionali possiamo avere un'altra versione.

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

Come Param1 è possibile aggiungere un numero qualsiasi di parametri per la condizione di ricerca.


0

Mi sono imbattuto in questo alla ricerca di qualcos'altro, ma ho pensato di inserire la versione lambda.

Innanzitutto, creerei una classe come questa per passare i parametri in un livello dati:

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

Quindi, nel mio livello dati, qualcosa del genere:

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

Il punto in cui materializzi la domanda dipende da te. Potrebbe avere un livello tra l'app e i dati che converte le rappresentazioni specifiche del database in db-agnostic (forse si interrogano più origini dati). Quel livello potrebbe ottenere più tipi di interrogabili da queste origini e mapparli a una rappresentazione POCO comune, ad esempio.


Oops, non ho visto la risposta di John Henckel. Stessa idea.
Scott Peterson

0

Solo per aggiungere qui la risposta accettata sopra , se stai facendo una ricerca dinamica su un join, considera di restituire un nuovo oggetto con entrambe le tabelle (t1, t2) nella query linq iniziale in modo da poter accedere individualmente per eseguire il condizionale ricerca.

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.

Ho ottenuto la risposta che stavo cercando qui per quanto riguarda l'unione di due tabelle e l'interrogazione di colonne specifiche su una delle tabelle

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.