2008-10-10 8 views
11

Supposons que mes objets sont en parfait état de fonctionnement (c'est-à-dire que TDD me fait penser qu'ils fonctionnent).Création d'une requête LINQ par programmation sans que les variables locales ne me trébuchent

J'ai une liste que je crée comme celui-ci (sauf dentelée correctement):

var result = from v in vendors 
      from p in v.Products 
      orderby p.Name 
      select p; 

Cela fonctionne - je reçois tous les produits de tous les fournisseurs.

Maintenant j'ai une liste de conditions, construites au moment de l'exécution par l'utilisateur. Appliquons-les:

foreach (Attribute a in requiredAttributes) 
{ 
    result = result.Where(p => p.Attributes.Contains(a)); 
} 

Cela peut être primitif, mais je pensais que cela fonctionnerait. Cependant, après que cette boucle foreach est terminée, lorsque vous énumérez "result", il contiendra tous les produits qui ont l'attribut LAST de la collection requiredAttributes dans sa propriété Attributes (également une collection). Pour moi, cela sent "a" est écrasé quelque part avec chaque voyage à travers la boucle, et seulement le dernier s'applique. À court d'écrire d'une manière ou d'une autre une méthode d'extension à IEnumerable appelée ContainsAll (IEnumerable) ou quelque chose à cet effet, comment puis-je réaliser ce que je veux, qui est essentiellement un ET logique, me donnant seulement les produits qui ont tous les attributs requis ?

+0

Merci les gars - Je donne la "réponse acceptée" à Jon car il semble qu'elle pourrait être plus appropriée pour le cas "général". Mais je vais certainement vérifier la suggestion d'Omer, je ne savais pas que vous pouviez le faire. Votes à tous. :) –

+0

La nouvelle façon de résoudre ce problème consiste simplement à utiliser C# 5. Voir aussi [L'utilisation des variables de foreach at-elle été modifiée dans C# 5?] (Http://stackoverflow.com/a/12112959/18192) – Brian

Répondre

19

(Edité par souci de clarté.)

Le problème est la boucle foreach, et le fait que la variable « a » est capturé et ensuite changé à chaque fois. Voici une modification qui fonctionnera, en introduisant effectivement une "nouvelle" variable pour chaque itération de la boucle, et en capturant cette nouvelle variable.

foreach (Attribute a in requiredAttributes) 
{ 
    Attribute copy = a; 
    result = result.Where(p => p.Attributes.Contains(copy)); 
} 

solution d'Omer est un nettoyant un si vous pouvez l'utiliser, mais cela peut aider si votre code réel est en fait plus compliqué :)

EDIT: Il y a plus sur la question this closures article - faites défiler jusqu'à "Comparaison des stratégies de capture: complexité vs puissance".

+0

J'ai appris cette nuance critique à partir des avertissements déroutants de ReSharper;) – Grank

7
var result = from v in vendors 
      from p in v.Products 
      where requiredAttributes.All(a => p.Attributes.Contains(a)) 
      orderby p.Name 
      select p; 

HTH.

5

Je n'ai pas codé vers le haut, mais le changement

foreach (Attribute a in requiredAttributes){  
    result = result.Where(p => p.Attributes.Contains(a)); 
} 

à

foreach (Attribute a in requiredAttributes){  
    Attribute b = a; 
    result = result.Where(p => p.Attributes.Contains(b)); 
} 

devrait fixer aussi, je pense.

Questions connexes