IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Linq Discussion :

Comportement différent selon le contexte


Sujet :

Linq

  1. #1
    Invité
    Invité(e)
    Par défaut Comportement différent selon le contexte
    Bonjour,

    J'ai constaté un comportement étrange sur une requête Linq To Entities.

    Pour l'histoire, j'ai une table contenant des clients avec, entre autre, un champ NomPrenom. L'utilisateur peut faire une recherche en saisissant un nom/prenom dans un seul champ. Pour que la recherche marche bien, on a fait une table de mot qui permet de savoir quels clients est concerné par chaque mot.

    Si l'utilisateur tape "Alain Dupont" (ou "Dupont Alain"), ça doit renvoyer tous les clients qui ont à la fois le mot Alain et le mot Dupont.

    Pour faire la recherche, j'avais donc fait ça dans ma fonction

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    foreach (var item in tabNomPrenom)
    {
        q = q.Where(ocn => ocn.Client.MotOes.Any(mot => mot.Lib.ToUpper().Contains(item.ToUpper()));
    }
    tabNomPrenom étant un tableau contenant tous les mots saisis par l'utilisateur.
    Le problème, c'est qu'au lieu de renvoyer les clients qui ont à la fois le mot "Alain" et le mot "Dupont", ça me renvoie les clients qui ont le mot "Alain" ou le mot "Dupont".

    Pour comprendre d'ou venait le problème, j'ai essayé de faire ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    q = q.Where(ocn => ocn.Occupant.MotOes.Any(mot => mot.Lib.ToUpper().Contains("ALAIN"))));
    q = q.Where(ocn => ocn.Occupant.MotOes.Any(mot => mot.Lib.ToUpper().Contains("DUPONT"))));
    Pour moi, c'est exactement la même chose mais décomposé au lieu d'être dans le foreach. Pourtant, à ma grande surprise, le résultat est différent. En faisant comme ça, j'ai bien les clients qui ont à la fois le mot "Alain" et le mot "Dupont".

    Quelqu'un pourrait m'expliquer le pourquoi. Je ne vois pas quelle est la différence entre mon foreach et mon deuxième code. Sur le papier, c'est pareil.

  2. #2
    Invité
    Invité(e)
    Par défaut
    Salut,

    Le problème est simple je pense : la fonction Contains teste si une chaîne de caractères est une sous chaîne d'une autre chaîne. Cette fonction ne se met à tester si chaque partie de la sous-chaîne est contenue dans la chaîne.

    Donc une fois que t'as compris cela, c'est à toi de "splitter" ta chaîne en mot et de tester si chaque mot est bien une partie de la chaîne source :
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    string[] mots = chaineRecherche.Split(' ');
    q = q.Where(ocn => ocn.Occupant.MotOes.Any(mot => mot.Trim().ToUpper() == mots[0].Trim().ToUpper() && mot.Trim().ToUpper() == mots[1].Trim().ToUpper())));
    Voilà là ça devrait marcher

  3. #3
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par h2s84 Voir le message
    Salut,

    Le problème est simple je pense : la fonction Contains teste si une chaîne de caractères est une sous chaîne d'une autre chaîne. Cette fonction ne se met à tester si chaque partie de la sous-chaîne est contenue dans la chaîne.

    Donc une fois que t'as compris cela, c'est à toi de "splitter" ta chaîne en mot et de tester si chaque mot est bien une partie de la chaîne source :
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    string[] mots = chaineRecherche.Split(' ');
    q = q.Where(ocn => ocn.Occupant.MotOes.Any(mot => mot.Trim().ToUpper() == mots[0].Trim().ToUpper() && mot.Trim().ToUpper() == mots[1].Trim().ToUpper())));
    Voilà là ça devrait marcher
    Le Split, c'est déjà ce que je fais. A l'origine, j'ai juste un string avec ce que l'utilisateur a saisi. Ensuite je fais :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    string[] tabNomPrenom = chaineRecherche.Split(' ');
    C'est ça que je place dans mon tabNomPrenom. Et ensuite, je fais simplement une boucle pour parcourir mon tableau. Donc, si mon tableau contient "Alain" et "Dupont", il y aura deux itérations dans la boucle, ce qui va entrainer deux passages sur :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    q = q.Where(ocn => ocn.Client.MotOes.Any(mot => mot.Lib.ToUpper().Contains(item.ToUpper()));
    Un premier passage pour le mot "Alain" :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    q = q.Where(ocn => ocn.Client.MotOes.Any(mot => mot.Lib.ToUpper().Contains("Alain".ToUpper()));
    Un second passage pour le mot "Dupont" :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    q = q.Where(ocn => ocn.Client.MotOes.Any(mot => mot.Lib.ToUpper().Contains("Dupont".ToUpper()));
    Ce qui donne en toute logique :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    q = q.Where(ocn => ocn.Client.MotOes.Any(mot => mot.Lib.ToUpper().Contains("ALAIN"))));
    q = q.Where(ocn => ocn.Client.MotOes.Any(mot => mot.Lib.ToUpper().Contains("DUPONT"))));
    Pourtant, le résultat renvoyé n'est pas comme prévu. Et par contre, si je tape directement le code ci-dessus, j'ai le bon résultat. C'est pas logique.

    Concernant la solution que tu proposes, ça doit fonctionner mais le problème est que je ne suis pas supposée connaitre le nombre d'éléments qu'il y a aura dans le tableau. Normalement, il devrait y en avoir 1 ou 2 mais bon, on ne sait jamais. Je trouve pas ça super d'écrire "en dur" mots[0], mots[1]. C'est pas assez flexible.

  4. #4
    Membre confirmé Avatar de NicoL__
    Homme Profil pro
    Architecte
    Inscrit en
    Janvier 2011
    Messages
    399
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Architecte

    Informations forums :
    Inscription : Janvier 2011
    Messages : 399
    Points : 577
    Points
    577
    Par défaut
    As-tu regardé dans SQL Profiler ce qui avait été généré par linq ?

  5. #5
    Membre expert
    Avatar de GuruuMeditation
    Homme Profil pro
    .Net Architect
    Inscrit en
    Octobre 2010
    Messages
    1 705
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : Belgique

    Informations professionnelles :
    Activité : .Net Architect
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2010
    Messages : 1 705
    Points : 3 568
    Points
    3 568
    Par défaut
    Est-ce que ce ne serait pas un problème de "closure"?

    Vu que la query est exécutée lorsque l'on regarde les résultats => elle est en fait executée hors de la boucle. Ce qui fait que tous les item utilisé dans le foreach va en fait pointer sur le dernier. En gros, il ne va chercher que le dernier nom dans le tableau chaineRecherche.

    A tester, pour éviter la closure :
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    foreach (var item in tabNomPrenom)
    {
    var currentname = item;
        q = q.Where(ocn => ocn.Client.MotOes.Any(mot => mot.Lib.ToUpper().Contains(currentname .ToUpper()));
    }
    Microsoft MVP : Windows Platform

    MCPD - Windows Phone Developer
    MCPD - Windows Developer 4

    http://www.guruumeditation.net

    “If debugging is the process of removing bugs, then programming must be the process of putting them in.”
    (Edsger W. Dijkstra)

  6. #6
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par GuruuMeditation Voir le message
    Est-ce que ce ne serait pas un problème de "closure"?

    Vu que la query est exécutée lorsque l'on regarde les résultats => elle est en fait executée hors de la boucle. Ce qui fait que tous les item utilisé dans le foreach va en fait pointer sur le dernier. En gros, il ne va chercher que le dernier nom dans le tableau chaineRecherche.

    A tester, pour éviter la closure :
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    foreach (var item in tabNomPrenom)
    {
    var currentname = item;
        q = q.Where(ocn => ocn.Client.MotOes.Any(mot => mot.Lib.ToUpper().Contains(currentname .ToUpper()));
    }
    Bien vu

    J'ai testé et c'est bien un problème de closure. Je connaissais ce problème mais j'y avais pas pensé pour ce cas précis.

    Merci pour ton aide !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. SimUnlockPhone comportement différent selon les téléphones
    Par bobby51 dans le forum Windows Mobile
    Réponses: 0
    Dernier message: 29/04/2011, 10h43
  2. [1.x] Backend:formulaire différent selon les contexts
    Par noreaga10 dans le forum Symfony
    Réponses: 11
    Dernier message: 06/05/2010, 12h23
  3. Réponses: 7
    Dernier message: 15/02/2010, 21h52
  4. Comportement différent selon le lanceur
    Par michelcky dans le forum GTK+ avec C & C++
    Réponses: 1
    Dernier message: 03/10/2008, 19h03
  5. Comportement différent selon Mode Execution
    Par MarronSuisse dans le forum C
    Réponses: 7
    Dernier message: 14/04/2008, 12h52

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo