Précédent   Forum des professionnels en informatique > Dotnet > Accès aux données > Linq
Linq Forum d'entraide sur la manipulation de données avec Linq
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Proposer ce sujet en actualité
 
Outils de la discussion
Publicité
'
Vieux 21/10/2011, 13h30   #1
Invité régulier
 
Aude Masly
Inscription : janvier 2010
Messages : 34
Détails du profil
Informations personnelles :
Nom : Aude Masly

Informations forums :
Inscription : janvier 2010
Messages : 34
Points : 7
Points : 7
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 :
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 :
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.
Sookie est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 21/10/2011, 14h13   #2
Modérateur
 
Avatar de h2s84
 
Homme Holty Samba SOW
Développeur .NET
Inscription : mars 2007
Messages : 2 393
Détails du profil
Informations personnelles :
Nom : Homme Holty Samba SOW
Âge : 27
Localisation : Sénégal

Informations professionnelles :
Activité : Développeur .NET
Secteur : High Tech - Éditeur de logiciels

Informations forums :
Inscription : mars 2007
Messages : 2 393
Points : 4 089
Points : 4 089
Envoyer un message via MSN à h2s84 Envoyer un message via Skype™ à h2s84
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# :
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
__________________
Les FAQs sur les technologies .Net voir ici
Les cours et tutos sur les technologies .Net voir ici
Les critiques sur les livres parlant des technologies .Net voir ici
Pensez à la balise [CODE]
Pensez au tag si votre problème est résolu

While(!success) { TryAgain(); }
h2s84 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 21/10/2011, 15h13   #3
Invité régulier
 
Aude Masly
Inscription : janvier 2010
Messages : 34
Détails du profil
Informations personnelles :
Nom : Aude Masly

Informations forums :
Inscription : janvier 2010
Messages : 34
Points : 7
Points : 7
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# :
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 :
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 :
q = q.Where(ocn => ocn.Client.MotOes.Any(mot => mot.Lib.ToUpper().Contains(item.ToUpper()));
Un premier passage pour le mot "Alain" :

Code :
q = q.Where(ocn => ocn.Client.MotOes.Any(mot => mot.Lib.ToUpper().Contains("Alain".ToUpper()));
Un second passage pour le mot "Dupont" :

Code :
q = q.Where(ocn => ocn.Client.MotOes.Any(mot => mot.Lib.ToUpper().Contains("Dupont".ToUpper()));
Ce qui donne en toute logique :

Code :
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.
Sookie est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 21/10/2011, 18h01   #4
Membre expérimenté
 
Avatar de NicoL__
 
Homme Nicolas
Inscription : janvier 2011
Messages : 378
Détails du profil
Informations personnelles :
Nom : Homme Nicolas
Localisation : France

Informations forums :
Inscription : janvier 2011
Messages : 378
Points : 528
Points : 528
As-tu regardé dans SQL Profiler ce qui avait été généré par linq ?
NicoL__ est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 21/10/2011, 18h44   #5
Membre Expert
 
Avatar de GuruuMeditation
 
Homme Olivier Matis
Software Architect
Inscription : octobre 2010
Messages : 767
Détails du profil
Informations personnelles :
Nom : Homme Olivier Matis
Âge : 37
Localisation : Belgique

Informations professionnelles :
Activité : Software Architect
Secteur : Conseil

Informations forums :
Inscription : octobre 2010
Messages : 767
Points : 1 577
Points : 1 577
Envoyer un message via MSN à GuruuMeditation
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# :
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 : Visual C#

MCPD - Windows Developer 4
MCPD - Web Developer 4
MCTS - Silverlight 4, Development

“If debugging is the process of removing bugs, then programming must be the process of putting them in.”
(Edsger W. Dijkstra)
GuruuMeditation est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 24/10/2011, 08h18   #6
Invité régulier
 
Aude Masly
Inscription : janvier 2010
Messages : 34
Détails du profil
Informations personnelles :
Nom : Aude Masly

Informations forums :
Inscription : janvier 2010
Messages : 34
Points : 7
Points : 7
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# :
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 !
Sookie est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Proposer ce sujet en actualité Cette discussion est résolue.
Outils de la discussion



Fuseau horaire GMT +2. Il est actuellement 07h38.


 
 
 
 
Partenaires

Hébergement Web