Linq: ajout de conditions à la clause where conditionnellement

103

J'ai une requête comme celle-ci

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

Je souhaite ajouter les différentes conditions telles que l'âge, la hauteur en fonction de si ces conditions ont été fournies à la méthode exécutant cette requête. Toutes les conditions incluront la division utilisateur. Si l'âge a été fourni, je veux l'ajouter à la requête. De même, si la hauteur a été fournie, je veux également l'ajouter.

Si cela devait être fait à l'aide de requêtes SQL, j'aurais utilisé le générateur de chaînes pour les ajouter à la requête strSQL principale. Mais ici, dans Linq, je ne peux penser qu'à utiliser une condition IF où j'écrirai la même requête trois fois, chaque bloc IF ayant une condition supplémentaire. Y a-t-il une meilleure manière de faire cela?

user20358
la source

Réponses:

182

Si vous n'appelez pas ToList()et votre mappage final au type DTO, vous pouvez ajouter des Whereclauses au fur et à mesure et générer les résultats à la fin:

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

Cela n'entraînera toujours qu'un seul appel à la base de données, ce qui sera effectivement tout aussi efficace que l'écriture de la requête en un seul passage.

Reed Copsey
la source
1
Dois-je mettre toutes les conditions where dans l'instruction "var query = .."?
user20358
4
Les conditions Where suivantes sont agrégées comme OU ou comme ET?
Vi100
4
@ vi100 ce seront des filtres supplémentaires, donc AND
Reed Copsey
Dieu merci pour la simplicité! J'en ai tellement marre de voir plus de 20 requêtes Linq ligne alors que ce qui précède est tellement plus lisible
justanotherdev
Pourquoi 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.
j'obtiens
19

J'utilise généralement le chaînage de méthodes mais j'ai le même problème. Et voici l'extension que j'utilise

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

Cela permet d'éviter les ruptures de chaîne. Aussi les mêmes ConditionalOrderByet ConditionalOrderByDescendingsont utiles.

Yuriy Granovskiy
la source
Utile, mais pourriez-vous s'il vous plaît ajouter un exemple de ce à quoi cela ressemblerait en cours d'utilisation.
Suncat2000
1
Cela devrait ressembler à: var fruits = wait db.Fruits .ConditionalWhere (() => color! = Null, f => f.Color == color) .ConditionalWhere (() => mûr! = Null, f => f .Ripe == mûr) .ToListAsync ();
Yuriy Granovskiy
4
Fonctionne très bien! Merci! J'ai également créé une surcharge pour la condition comme une simple valeur booléenne au lieu d'une fonction, pour la rendre plus intuitive lorsqu'un délégué ajouterait une complexité inutile. J'utilise cette méthode d'extension assez fréquemment maintenant et j'apprécie beaucoup votre solution.
Suncat2000
18

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

ou vous pouvez passer à la syntaxe de méthode pour linq et utiliser des conditions if pour attacher des expressions à la clause where.

Matthieu Vines
la source
3

Simplement, je l'utilise dans ma clause where comme

    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();
}
Posséder
la source
3

En fonction de certaines conditions, ajoutez la condition 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();
Melu
la source
2

Voici mon code pour faire une chose similaire. Il s'agit d'une méthode sur mon API de service 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 requête de base est Select(f => f)ce qui signifie fondamentalement TOUT, et les Whereclauses y sont éventuellement attachées. La finale Selectest facultative. J'utilise pour convertir les objets de lignes de base de données en objets de résultat "Fruit".

John Henckel
la source
0

En supposant le paramètre suivant,

Int? Age = 18;

En utilisant simplement &&et des ||opérateurs conditionnels, nous pouvons avoir une autre version.

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

Comme Param1, vous pouvez ajouter n'importe quel nombre de paramètres pour la condition de recherche.

Sushant Yelpale
la source
0

Je viens de tomber sur cela à la recherche de quelque chose d'autre, mais j'ai pensé que je jetterais dans la version lambda.

Tout d'abord, je créerais une classe comme celle-ci pour passer des paramètres dans une couche de données:

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

Ensuite, dans ma couche de données, quelque chose comme ceci:

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

L'endroit où vous matérialisez la requête dépend de vous. Il peut y avoir une couche entre l'application et les données qui convertit les représentations spécifiques à la base de données en représentations indépendantes de la base de données (peut-être que vous interrogez plusieurs sources de données). Cette couche peut obtenir plusieurs types d'éléments interrogeables à partir de ces sources et les mapper à une représentation POCO commune, par exemple.

Scott Peterson
la source
Oups, je n'ai pas vu la réponse de John Henckel. Même idée.
Scott Peterson
0

Juste pour ajouter à la réponse acceptée ci-dessus ici , si vous effectuez une recherche dynamique sur une jointure, envisagez de renvoyer un nouvel objet avec les deux tables (t1, t2) dans la requête linq initiale afin que vous puissiez y accéder individuellement pour faire le conditionnel chercher.

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.

J'ai eu la réponse que je cherchais ici en ce qui concerne la jonction de deux tables et l'interrogation de colonnes spécifiques sur l'une ou l'autre des tables

Riaan Saayman
la source