Je viens d'avoir l'expérience de débogage la plus étrange depuis très longtemps. C'est un peu embarrassant d'admettre, mais cela m'amène à croire que ma requête Linq produit PLUS de résultats lors de l'ajout d'une clause Where supplémentaire.Pourquoi ma clause Linq Where produit-elle plus de résultats au lieu de moins?
Je sais que ce n'est pas possible, donc je l'ai refactorisé ma fonction incriminée ainsi que le test unitaire appartenant dans ceci:
[Test]
public void LoadUserBySearchString()
{
//Setup
var AllUsers = new List<User>
{
new User
{
FirstName = "Luke",
LastName = "Skywalker",
Email = "[email protected]"
},
new User
{
FirstName = "Leia",
LastName = "Skywalker",
Email = "[email protected]"
}
};
//Execution
List<User> SearchResults = LoadUserBySearchString("princess", AllUsers.AsQueryable());
List<User> SearchResults2 = LoadUserBySearchString("princess Skywalker", AllUsers.AsQueryable());
//Assertion
Assert.AreEqual(1, SearchResults.Count); //test passed!
Assert.AreEqual(1, SearchResults2.Count); //test failed! got 2 instead of 1 User???
}
//search CustID, fname, lname, email for substring(s)
public List<User> LoadUserBySearchString(string SearchString, IQueryable<User> AllUsers)
{
IQueryable<User> Result = AllUsers;
//split into substrings and apply each substring as additional search criterium
foreach (string SubString in Regex.Split(SearchString, " "))
{
int SubStringAsInteger = -1;
if (SubString.IsInteger())
{
SubStringAsInteger = Convert.ToInt32(SubString);
}
if (SubString != null && SubString.Length > 0)
{
Result = Result.Where(c => (c.FirstName.Contains(SubString)
|| c.LastName.Contains(SubString)
|| c.Email.Contains(SubString)
|| (c.ID == SubStringAsInteger)
));
}
}
return Result.ToList();
}
Je déboguée la fonction LoadUserBySearchString et a affirmé que le second appel à la fonction produit en fait une requête linq avec deux clauses where au lieu d'une. Il semble donc que la clause where supplémentaire augmente le nombre de résultats.
Ce qui est encore plus bizarre, la fonction LoadUserBySearchString fonctionne très bien lorsque je la teste à la main (avec de vrais utilisateurs de la base de données). Il montre seulement ce comportement bizarre lors de l'exécution du test unitaire. Je suppose que j'ai juste besoin de dormir (ou même de longues vacances). Si quelqu'un pouvait m'aider à faire la lumière sur ce sujet, je pourrais arrêter d'interroger ma santé mentale et retourner au travail.
Merci,
Adrian
Modifier (pour clarifier plusieurs réponses que je vais à ce jour): Je sais qu'il ressemble à c'est la ou clause, mais unfortuantely ce n'est pas aussi simple que cela. LoadUserBySearchString divise la chaîne de recherche en plusieurs chaînes et attache une clause Where à chacune d'entre elles. "Skywalker" correspond à la fois à Luke et à Leia, mais "princesse" ne correspond qu'à Leia.
Ceci est la requête Linq pour la chaîne de recherche "princesse":
+ Result {System.Collections.Generic.List`1[TestProject.Models.User].Where(c => (((c.FirstName.Contains(value(TestProject.Controllers.SearchController+<>c__DisplayClass1).SubString) || c.LastName.Contains(value(TestProject.Controllers.SearchController+<>c__DisplayClass1).SubString)) || c.Email.Contains(value(TestProject.Controllers.SearchController+<>c__DisplayClass1).SubString)) || (c.ID = value(TestProject.Controllers.SearchController+<>c__DisplayClass3).SubStringAsInteger)))} System.Linq.IQueryable<TestProject.Models.User> {System.Linq.EnumerableQuery<TestProject.Models.User>}
Et c'est la clause Linq pour la chaîne de recherche "princesse Skywalker"
+ Result {System.Collections.Generic.List`1[TestProject.Models.User].Where(c => (((c.FirstName.Contains(value(TestProject.Controllers.SearchController+<>c__DisplayClass1).SubString) || c.LastName.Contains(value(TestProject.Controllers.SearchController+<>c__DisplayClass1).SubString)) || c.Email.Contains(value(TestProject.Controllers.SearchController+<>c__DisplayClass1).SubString)) || (c.ID = value(TestProject.Controllers.SearchController+<>c__DisplayClass3).SubStringAsInteger))).Where(c => (((c.FirstName.Contains(value(TestProject.Controllers.SearchController+<>c__DisplayClass1).SubString) || c.LastName.Contains(value(TestProject.Controllers.SearchController+<>c__DisplayClass1).SubString)) || c.Email.Contains(value(TestProject.Controllers.SearchController+<>c__DisplayClass1).SubString)) || (c.ID = value(TestProject.Controllers.SearchController+<>c__DisplayClass3).SubStringAsInteger)))} System.Linq.IQueryable<TestProject.Models.User> {System.Linq.EnumerableQuery<TestProject.Models.User>}
comme ci-dessus, juste avec une clause where supplémentaire.
Merci beaucoup! Vous avez fait ma journée :-) –
+1, en utilisant une variable locale aide généralement à résoudre les problèmes de fermeture. Pour en savoir plus sur l'utilisation des fermetures dans LINQ: http://diditwith.net/2007/09/25/LINQClosuresMayBeHazardousToYourHealth.aspx – Lucas