web-dev-qa-db-fra.com

LINQ: ajout de la clause where uniquement lorsqu'une valeur n'est pas nulle

Je sais qu'un moyen typique est comme ça:

IQueryable query = from staff in dataContext.Staffs;
if(name1 != null)
{
     query = from staff in query where (staff.name == name1);
}

Cependant, à partir d'un programme que nous avons repris d'autres développeurs, nous avons vu un code comme celui-ci:

IQueryable query = from staff in dataContext.Staffs;
query = from staff in query where (name1 == null || staff.name == name1);

S'il s'agit d'une instruction SQL normale, je dirais certainement que la deuxième est une mauvaise pratique. Parce qu'il ajoute une clause where sans signification à la requête lorsque name1 est null.

Mais je suis nouveau sur LINQ, donc je ne suis pas sûr si LINQ est différent? 

19
Henry

vous pouvez l'écrire comme

IQueryable query = from staff in dataContext.Staffs;
query = from staff in query where (name1 != null && staff.name == name1);

De cette façon, la seconde partie de votre condition ne sera pas évaluée si votre première condition est évaluée à false

Mettre à jour:
si vous écrivez

IQueryable query = from staff in dataContext.Staffs;
    query = from staff in query where (name1 == null || staff.name == name1);

et name1 est null, la seconde partie de votre condition ne sera pas évaluée car une condition ne nécessite qu'une condition pour renvoyer true

svp voir ceci link pour plus de détails

18

Souvent, ce genre de chose semble plus facile à écrire en utilisant la syntaxe fluide, plutôt que la syntaxe de requête.

par exemple.

IQueryable query = dataContext.Staffs;
if(name1 != null)
{
     query = query.Where(x => x.name == name1);
}

Donc, si name1 est null, vous ne faites aucun appel Where(). Si vous avez plusieurs filtres différents, qui peuvent tous être requis ou non, et peut-être divers ordres de tri, je trouve que cela devient beaucoup plus gérable.

Edit for alex: OK, je répondais à la question sur l'ajout d'une clause where uniquement lorsqu'une valeur n'est pas nulle. En réponse à l’autre partie de la question, j’ai essayé ceci avec Entity Framework 4 pour voir quel SQL produit par LINQ. Pour ce faire, transformez query en ObjectQuery et appelez .ToTraceString(). Les résultats ont été que la clause WHERE est sortie comme suit:

WHERE @p__linq__0 IS NULL OR [Extent1].[name] = @p__linq__1

Donc, oui, c'est du mauvais SQL classique, si vous avez un index sur la colonne name, ne vous attendez pas à ce qu'il soit utilisé.

(Edit # 2:} _ Essayez à nouveau d'utiliser LINQ to SQL plutôt que Entity Framework, avec des résultats assez différents. Cette fois-ci, essayer la requête avec name1 nul, ne donne aucune clause WHERE, comme vous le souhaiteriez; l’essayer avec name1 étant "a" aboutissait à un simple WHERE [t0].[name] = @p0 et @p0 envoyé comme "a". Entity Framework ne semble pas optimiser ainsi. C'est un peu inquiétant.

10
Carson63000

La meilleure façon de faire est de créer vous-même une méthode extension qui prendra une déclaration conditionnelle et une expression where. Si la condition est vraie, il utilisera l'expression où sinon, il ne l'utilisera pas. Cela peut considérablement nettoyer votre code, éliminant ainsi le besoin d’instructions if. 

public static class LinqExtensions
{
    public static IQueryable<T> WhereIf<T>(this IQueryable<T> query, bool condition, Expression<Func<T, bool>> whereClause)
    {
        if (condition)
        {
            return query.Where(whereClause);
        }
        return query;
    }
}

Maintenant, vous pouvez écrire votre code comme ceci:

IQueryable<Staffs> query = dataContext.Staffs.AsQueryable().WhereIf(name1 != null, x => x.Name == name1);
3
Soto

LINQ est différent dans certaines autres causes (pas dans cette cause), LINQ est le moyen d'obtenir des données de la "manière la plus rapide" avec un code mineur et un code clair autant que possible, ce qui présente de nombreux avantages:

  1. Facilite la transformation des données en objets. Je suis sûr que vous avez souvent entendu le terme "incompatibilité d'impédence", ce qui signifie que LINQ réduit la quantité de travail que vous devez effectuer pour effectuer la traduction entre le code orienté objet et les paradigmes de données tels que hiérarchique, fichier plat, messages, etc. relationnel, et plus. Cela n'élimine pas le "décalage d'impédence" car vous devez toujours raisonner sur vos données dans leur forme native, mais le pont d'ici à là-bas (IMO) est beaucoup plus court.

  2. Une syntaxe commune pour toutes les données. Une fois que vous avez appris la syntaxe d'une requête, vous pouvez l'utiliser avec n'importe quel fournisseur LINQ. Je pense que c'est un paradigme de développement bien meilleur que la tour de Babel qui s'est développée au fil des ans avec les technologies d'accès aux données. Bien entendu, chaque fournisseur LINQ a des nuances uniques nécessaires, mais la syntaxe de base et la syntaxe de requête sont les mêmes.

  3. Code fortement typé. La syntaxe de la requête C # (ou VB.NET) fait partie du langage et vous codez avec des types C #, qui sont traduits en quelque chose que le fournisseur comprend. Cela signifie que vous gagnez en productivité en permettant au compilateur de rechercher les erreurs plus tôt dans le cycle de vie du développement qu’ailleurs. Cela dit, de nombreuses erreurs dans la syntaxe de procédure stockée génèrent des erreurs lorsque vous enregistrez, mais LINQ est plus général que SQL Server. Vous devez penser à tous les autres types de sources de données qui génèrent des erreurs d’exécution, car leurs requêtes sont formées avec des chaînes ou un autre mécanisme mal typé.

  4. Intégration du fournisseur. Rassembler des sources de données est très facile. Par exemple, vous pouvez utiliser LINQ to Objects, LINQ to SQL et LINQ to XML ensemble pour des scénarios très sophistiqués. Je pense que c'est très élégant.

  5. Réduction du travail. Avant LINQ, je passais beaucoup de temps à créer des DAL, mais maintenant, mon DataContext est le DAL. J'ai aussi utilisé des fichiers OPF, mais maintenant j'ai LINQ qui est livré avec plusieurs fournisseurs dans la boîte et de nombreux autres fournisseurs tiers, me donnant les avantages de mes précédents points. Je peux configurer un DataContext LINQ to SQL en une minute (aussi rapidement que mon ordinateur et IDE peuvent le suivre).

  6. La performance dans le cas général ne devient pas un problème. SQL Server optimise assez bien les requêtes de nos jours, tout comme les processus stockés. Bien sûr, il existe encore des cas où les procédures stockées sont nécessaires pour des raisons de performances. Par exemple, j'ai trouvé plus intelligent d'utiliser un proc stocké lorsque j'avais plusieurs interactions entre des tables avec une logique supplémentaire à l'intérieur d'une transaction. La surcharge de communication liée à la tentative de réaliser la même tâche dans le code, en plus de l'implication du DTC dans une transaction distribuée, a rendu le choix d'un processus stocké plus convaincant. Cependant, pour une requête qui s'exécute en une seule instruction, LINQ est mon choix préféré, car même s'il y avait un léger gain de performances d'un processus stocké, les avantages des points précédents (IMO) ont plus de poids.

  7. Sécurité intégrée. Une des raisons pour lesquelles j'ai préféré les procédures stockées avant LINQ était qu'elles obligeaient l'utilisation de paramètres, contribuant ainsi à réduire les attaques par injection SQL. LINQ to SQL paramètre déjà l'entrée, qui est tout aussi sécurisée.

  8. LINQ est déclaratif. Nous travaillons beaucoup avec LINQ to XML ou LINQ to SQL, mais LINQ to Objects est incroyablement puissant. Un exemple typique de LINQ to Objects est la lecture d'éléments d'une chaîne []. Cependant, ce n'est qu'un petit exemple. Si vous pensez à toutes les collections IEnumerable (vous pouvez également interroger IEnumerable) avec lesquelles vous travaillez tous les jours, les opportunités sont nombreuses. c'est-à-dire rechercher un contrôle ListBox dans ASP.NET pour les éléments sélectionnés, effectuer des opérations définies (telles que Union) sur deux collections ou effectuer une itération dans une liste et exécuter un lambda dans un ForEach de chaque élément. Une fois que vous commencez à penser dans LINQ, qui est de nature déclarative, vous pouvez trouver que beaucoup de vos tâches sont plus simples et plus intuitives que les techniques impératives que vous utilisez aujourd'hui.

Je pourrais probablement continuer, mais je ferais mieux de m'arrêter là. Espérons que cela fournira une vision plus positive de la façon dont vous pourriez être plus productif avec LINQ et peut-être y voir une technologie utile dans une perspective plus large.

1
Jacob

J'ai donc essayé la méthode d'extension .Where(..., x => ...) indiquée ici comme réponse, mais cela ne fonctionne pas avec Entity Framework, car Linq To Entities ne sait pas le traduire en TSQL. 

Alors voici ma solution pour obtenir mon Func sur:

Expression<Func<SomeEfPoco, bool>> columnBeingFilteredPredicate = x => true; // Default expression to just say yes
if (!string.IsNullOrWhiteSpace(someColumnBeingFilteredValue))
{
    columnBeingFilteredPredicate = x => x.someColumnBeingFiltered == someColumnBeingFilteredValue;
}

_context.SomeEfPocos.Where(x => ..... &&
            ..... &&
            ..... &&)
.Where(columnBeingFilteredPredicate);

someColumnBeingFilteredValue dans mon cas est un paramètre de chaîne sur la méthode d'encapsulation avec une valeur par défaut de NULL.

1
Stephen York

J'ai vu ce modèle en SQL standard, et cela semble utile si vous avez plusieurs paramètres pouvant être NULL. Par exemple:

SELECT * FROM People WHERE ( @FirstName IS NULL OR FirstName = @FirstName )
                       AND ( @LastName IS NULL OR LastName = @LastName )

Si vous voyez cela dans LINQ, il est possible qu’ils traduisent aveuglément leurs anciennes requêtes SQL.

1
Danko Durbić

J'aime utiliser l'expression Par exemple.

    Expression<Func<Persons, bool>> expresionFinal = c => c.Active == true;

    if (DateBirth.HasValue)
                {
                    Expression<Func<Persons, bool>> expresionDate = c => (EntityFunctions.TruncateTime(c.DateBirth) == DateBirth);
                    expresionFinal = PredicateBuilder.And(expresionFinal, expresionDate);
                }

IQueryable query = dataContext.Persons;
 query = query.Where(expresionFinal);
1
J4ime

Pour EF Core, j'ai cassé ça comme ça:

IQueryable<Partners> recs = contextApi.Partners;
if (status != -1)
{
   recs = recs.Where(i => i.Status == status);
}
recs = recs.OrderBy(i => i.Status).ThenBy(i => i.CompanyName);
foreach (var rec in recs)
{
}

Je devais être explicite avec ma frappe au lieu de compter sur var.

0
Chad Kuehn

Non, je ne suis pas tout à fait d’accord avec vous . Ici vous venez de donner une logique simple 

if(name1 != null)
// do your stuff

mais que se passera-t-il si vous faites quelque chose de différent avec name1 qui ont une valeur null .. !! Ok. Considérons maintenant cette situation . Dans cet exemple, vous montrez comment gérer des valeurs null possibles dans des collections source .Une collection d'objets telle que IEnumerable<T> peut contenir des éléments dont la valeur est null . Si une collection source est null ou contient un élément dont la valeur est null, .__ et que votre requête ne gère pas les valeurs null, une NullReferenceException sera jeté lorsque vous exécutez la requête.

Cela pourrait probablement être un problème ... 

0
PawanS