2008-08-14 30 views
78

Nous travaillons sur un Log Viewer. L'utilisation aura l'option de filtrer par utilisateur, la gravité, etc. Dans les jours Sql, je voudrais ajouter à la chaîne de requête, mais je veux le faire avec Linq. Comment puis-je ajouter conditionnellement where-clauses?Requêtes Linq conditionnelles

Répondre

138

si vous souhaitez filtrer que si certains critères est passé, quelque chose comme ça

var logs = from log in context.Logs 
      select log; 

if (filterBySeverity) 
    logs = logs.Where(p => p.Severity == severity); 

if (filterByUser) 
    logs = logs.Where(p => p.User == user); 

Ce faisant, cette façon permettra à votre arbre d'expression soit exactement ce que vous voulez. De cette façon, le SQL créé sera exactement ce dont vous avez besoin et rien de moins.

+2

Salut Avez-vous des suggestions pour faire les clauses where au lieu de ANDs ..? –

+1

Ouais ... c'est un peu difficile à faire. Le meilleur que j'ai vu est à travers le modèle de spécification et en tirant le prédicat dans la spécification, puis en appelant spécification.Or (someOtherSpecification). Fondamentalement, vous devez écrire votre propre arbre d'expression un peu. Exemple de code et d'explication ici: http://codeinsanity.com/archive/2008/08/13/implementing-repository-and-specification-patterns-using-linq.aspx –

+0

J'ai une question stupide, Si ces journaux sont acquis à partir de la base de données, recevons-nous tous les journaux, puis les filtrons en mémoire? Si oui, comment puis-je passer les conditions à la base de données –

0

Il suffit d'utiliser C# 's & & opérateur:

var items = dc.Users.Where(l => l.Date == DateTime.Today && l.Severity == "Critical") 

Edit: Ah, besoin de lire plus attentivement. Vous voulez savoir comment sous condition ajouter des clauses supplémentaires. Dans ce cas, je n'en ai aucune idée. :) Ce que je ferais probablement est juste de préparer plusieurs requêtes, et d'exécuter la bonne, en fonction de ce que j'ai fini par avoir besoin.

0

Vous pouvez utiliser une méthode externe:

var results = 
    from rec in GetSomeRecs() 
    where ConditionalCheck(rec) 
    select rec; 

... 

bool ConditionalCheck(typeofRec input) { 
    ... 
} 

Cela fonctionnerait, mais ne peut être décomposé en arbres d'expression, ce qui signifie que LINQ to SQL courrait le code de vérification contre tous les records.

Autre possibilité:

var results = 
    from rec in GetSomeRecs() 
    where 
     (!filterBySeverity || rec.Severity == severity) && 
     (!filterByUser|| rec.User == user) 
    select rec; 

Cela pourrait fonctionner dans les arbres d'expression, ce qui signifie LINQ to SQL serait optimisé.

0

Eh bien, ce que je pensais que vous pourriez mettre les conditions de filtre dans une liste générique de prédicats:

var list = new List<string> { "me", "you", "meyou", "mow" }; 

    var predicates = new List<Predicate<string>>(); 

    predicates.Add(i => i.Contains("me")); 
    predicates.Add(i => i.EndsWith("w")); 

    var results = new List<string>(); 

    foreach (var p in predicates) 
     results.AddRange(from i in list where p.Invoke(i) select i);    

qui se traduit par une liste contenant « moi », « meyou » et « faucher ».

Vous pouvez optimiser cela en faisant le foreach avec les prédicats dans une fonction totalement différente qui OR tous les prédicats.

1

Ce n'est pas la chose la plus jolie mais vous pouvez utiliser une expression lambda et passer vos conditions en option. En TSQL je fais beaucoup de ce qui suit pour faire des paramètres en option:

où le champ = @FieldVar OU @FieldVar IS NULL

Vous pouvez dupliquer le même style avec un lambda suivant (un exemple de vérification de l'authentification):

MyDataContext db = new MyDataContext();

RunQuery void (string param1, param2 chaîne, int? Param3) {

Func checkuser = user =>

((param1.Length> 0)? Utilisateur.Param1 == param1: 1 == 1) & &

((param2.Length> 0) user.Param2 == param2: 1 == 1) & &

(! (Param3 = null) ? user.Param3 == param3: 1 == 1);

Utilisateur trouvéUser = db.Users.SingleOrDefault (checkUser);

}

13

En matière de LINQ conditionnelle, je suis très friands du motif des filtres et des tuyaux.
http://blog.wekeroad.com/mvc-storefront/mvcstore-part-3/

Fondamentalement, vous créez une méthode d'extension pour chaque cas de filtre qui accepte IQueryable et un paramètre.

public static IQueryable<Type> HasID(this IQueryable<Type> query, long? id) 
{ 
    return id.HasValue ? query.Where(o => i.ID.Equals(id.Value)) : query; 
} 
3

Une autre option serait d'utiliser quelque chose comme le PredicateBuilder discuté here. Il vous permet d'écrire du code comme le suivant:

var newKids = Product.ContainsInDescription ("BlackBerry", "iPhone"); 

var classics = Product.ContainsInDescription ("Nokia", "Ericsson") 
        .And (Product.IsSelling()); 

var query = from p in Data.Products.Where (newKids.Or (classics)) 
      select p; 

Notez que je n'ai que cela fonctionne avec Linq 2 SQL. EntityFramework n'implémente pas Expression.Invoke, qui est requis pour que cette méthode fonctionne. J'ai une question à ce sujet here.

+0

Ceci est une excellente méthode pour ceux qui utilisent un Business Logic Layer sur leur référentiel avec un outil comme AutoMapper pour mapper entre le transfert de données objets et modèles Entity. L'utilisation du générateur de prédicats vous permettra de modifier dynamiquement votre IQueryable avant de l'envoyer à AutoMapper pour aplatir, c'est-à-dire de mettre la liste en mémoire. Notez qu'il prend également en charge Entity Framework. – chrisjsherm

1

J'ai eu une exigence similaire récemment et finalement trouvé dans le MSDN. CSharp Samples for Visual Studio 2008

Les classes incluses dans l'échantillon DynamicQuery du téléchargement vous permettent de créer des requêtes dynamiques lors de l'exécution dans le format suivant:

var query = 
db.Customers. 
Where("City = @0 and Orders.Count >= @1", "London", 10). 
OrderBy("CompanyName"). 
Select("new(CompanyName as Name, Phone)"); 

L'utilisation de ce que vous pouvez construire une chaîne de requête dynamiquement lors de l'exécution et le transmettre dans la méthode where():

string dynamicQueryString = "City = \"London\" and Order.Count >= 10"; 
var q = from c in db.Customers.Where(queryString, null) 
     orderby c.CompanyName 
     select c; 
19

j'ai fini en utilisant une réponse similaire à Daren de, mais avec une interface IQueryable:

IQueryable<Log> matches = m_Locator.Logs; 

// Users filter 
if (usersFilter) 
    matches = matches.Where(l => l.UserName == comboBoxUsers.Text); 

// Severity filter 
if (severityFilter) 
    matches = matches.Where(l => l.Severity == comboBoxSeverity.Text); 

Logs = (from log in matches 
     orderby log.EventTime descending 
     select log).ToList(); 

Cela construit la requête avant de frapper la base de données. La commande ne s'exécutera pas jusqu'à .ToList() à la fin.

21

Si vous avez besoin de filtrer la base sur une liste/Array, utilisez les éléments suivants:

public List<Data> GetData(List<string> Numbers, List<string> Letters) 
    { 
     if (Numbers == null) 
      Numbers = new List<string>(); 

     if (Letters == null) 
      Letters = new List<string>(); 

     var q = from d in database.table 
       where (Numbers.Count == 0 || Numbers.Contains(d.Number)) 
       where (Letters.Count == 0 || Letters.Contains(d.Letter)) 
       select new Data 
       { 
        Number = d.Number, 
        Letter = d.Letter, 
       }; 
     return q.ToList(); 

    } 
+3

C'est de loin la meilleure et la meilleure réponse. Le conditionnel || compare seulement la première partie et saute la seconde si la première partie est vraie ... bien faite! –

+0

Cette construction inclut la partie 'ou' de l'expression dans la requête SQL générée. La réponse acceptée générera des déclarations plus efficaces. En fonction des optimisations du fournisseur de données, bien sûr. LINQ-to-SQL peut avoir une meilleure optimisation, mais pas LINQ-to-Entities. – Suncat2000

3

Faire ceci:

bool lastNameSearch = true/false; // depending if they want to search by last name, 

ayant dans la déclaration where:

where (lastNameSearch && name.LastNameSearch == "smith") 

signifie que lorsque la requête finale est créée, si lastNameSearch est false la requête omettra complètement tout SQL pour la recherche de nom de famille.

+0

Dépend du fournisseur de données. LINQ-to-Entities ne l'optimise pas si bien. – Suncat2000