繁体   English   中英

为什么我的Linq Where子句产生的结果多而不是少?

[英]Why does my Linq Where clause produce more results instead of less?

我只是很长时间以来最奇怪的调试经验。 承认这有点尴尬,但这使我相信,当添加其他Where子句时,我的Linq查询会产生更多结果。

我知道这是不可能的,因此我将有问题的功能以及属于它的单元测试重构为:

[Test]
public void LoadUserBySearchString()
{
    //Setup
    var AllUsers = new List<User>
                       {
                           new User
                               {
                                   FirstName = "Luke",
                                   LastName = "Skywalker",
                                   Email = "luke@jedinet.org"
                               },
                           new User
                               {
                                   FirstName = "Leia",
                                   LastName = "Skywalker",
                                   Email = "faeryprincess@winxmail.com"
                               }
                       };


    //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();
}

我已经调试了LoadUserBySearchString函数,并断言对该函数的第二次调用实际上产生了一个带有两个where子句而不是一个子句的linq查询。 因此,似乎附加的where子句增加了结果的数量。

更奇怪的是,LoadUserBySearchString函数在我手动测试时(与数据库中的实际用户一起使用)效果很好。 它仅在运行单元测试时显示这种奇怪的行为。

我想我只需要睡觉(甚至需要延长假期)。 如果有人可以帮助我阐明这一点,我可以停止质疑我的理智,然后重新开始工作。

谢谢,

阿德里安

编辑(以澄清到目前为止我的一些回答) :我知道它看起来像是or子句,但是不幸的是,它并不是那么简单。 LoadUserBySearchString将搜索字符串分成几个字符串,并为每个字符串附加一个Where子句。 “天行者”同时匹配卢克和莉亚,但“公主”仅匹配莉亚。

这是搜索字符串“ princess”的Linq查询:

+       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>}

这是搜索字符串“ princess Skywalker”的Linq子句

+       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>}

与上述相同,只是增加了一个where子句。

这是一个不错的小陷阱。

发生的事情是,由于匿名方法和延迟执行,您实际上并没有过滤“公主”。 相反,您正在构建一个过滤器,该过滤器将对subString变量的内容进行过滤。

但是,然后您更改此变量,并构建另一个过滤器,该过滤器再次使用相同的变量。

基本上,这就是您要执行的简短形式:

Where(...contains(SubString)).Where(...contains(SubString))

因此,实际上,您仅对两个单词中都存在的最后一个单词进行过滤,这仅仅是因为在实际应用这些过滤器时,只剩下一个SubString值,即最后一个。

如果更改代码,以便在循环范围内捕获SubString变量,则它将起作用:

if (SubString != null && SubString.Length > 0)
{
    String captured = SubString;
    Int32 capturedId = SubStringAsInteger;
    Result = Result.Where(c => (c.FirstName.Contains(captured)
                                || c.LastName.Contains(captured)
                                || c.Email.Contains(captured)
                                || (c.ID == capturedId)
                               ));
}

您的算法相当于“选择与搜索字符串中的任何单词匹配的记录”。

这是由于推迟执行。 直到您调用.ToList()才真正执行查询。 如果在循环内移动.ToList(),则将获得所需的行为。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM