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

Adr*_*ore 2 c# linq linq-to-entities

我在很长一段时间内获得了最奇怪的调试体验.承认这有点令人尴尬,但它让我相信我的Linq查询在添加额外的Where子句时会产生更多结果.

我知道这是不可能的,所以我重构了我的违规功能加上属于它的单元测试:

[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();
}
Run Code Online (Sandbox Code Playgroud)

我调试了LoadUserBySearchString函数并断言第二次调用函数实际上产生了一个带有两个where子句而不是一个的linq查询.所以似乎额外的where子句增加了结果的数量.

更奇怪的是,当我手工测试它时(与来自数据库的真实用户),LoadUserBySearchString函数非常有用.它只显示运行单元测试时的这种奇怪行为.

我想我只需要一些睡眠(甚至是长假).如果有人能帮我解释一下,我可以不再质疑我的理智并重新开始工作.

谢谢,

阿德里安

编辑(澄清我到目前为止的几个回复):我知道它看起来像是条款,但不幸的是它并不那么简单.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>}
Run Code Online (Sandbox Code Playgroud)

这是搜索字符串"公主天行者"的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>}
Run Code Online (Sandbox Code Playgroud)

与上面相同,只需添加一个where子句.

ang*_*son 6

这是一个不错的小问题.

发生的事情是,由于匿名方法和延迟执行,你实际上并没有过滤"公主".相反,您正在构建一个过滤器,用于过滤subString变量的内容.

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

基本上,这是您将以简短形式执行的内容:

Where(...contains(SubString)).Where(...contains(SubString))
Run Code Online (Sandbox Code Playgroud)

所以,你实际上只是对两个中存在的最后一个词进行过滤,仅仅因为在实际应用这些过滤器时,只剩下一个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)
                               ));
}
Run Code Online (Sandbox Code Playgroud)