下面是我的代码的简化版本。我希望 p1p2 相等,而且 p1_afterp2_after 也相等,因为 GetPerson1()GetPerson2() 之间的唯一区别是 .ToList() 子句强制执行查询,而没有对选择标准进行任何更改。

我的假设不正确吗?我在我的程序中发现了一个错误,归结为 p1_afterp2_after 不同(由于年龄已更改为 26,因此 p2_after 为空,但 p1_after 仍包含与 p1 相同的实例)。

这种行为正常吗?这对我来说似乎不合逻辑,这就是我想检查的原因。尤其是当 p1_after.Age 返回 26 时,尽管已选择 p1_after 使其 Age 为 25。

    public void OnGet()
    {
        Person p1 = GetPerson1();
        Person p2 = GetPerson2();

        p1.Age = 26;
        p2.Age = 26;

        Person p1_after = GetPerson1(); // not null, but p1_after.Age is 26
        Person p2_after = GetPerson2(); // null
    }

    public Person GetPerson1()
    {
        return _context
            .Persons
            .Where(p => p.Age == 25)
            .SingleOrDefault();
    }

    public Person GetPerson2()
    {
        return _context
            .Persons
            .ToList()
            .Where(p => p.Age == 25)
            .SingleOrDefault();
    }

最佳答案

这是一个疯狂的猜测,但我有一个假设,为什么你的程序会这样。

更改年龄后,您没有调用任何 SaveChanges/SaveChangesAsync 方法,因此您的更改不会反射(reflect)在数据库中,而只会反射(reflect)到您的代码中。

现在,当您再次调用 GetPerson1 方法时,您要求从数据库中获取年龄为 25 岁的人,并且由于您的更改未反射(reflect)数据库,因此您将获得与以前相同的结果。
奇怪的部分是为什么当您调用 GetPerson2 方法时会得到不同的结果,这是我的猜测 - 在 GetPerson2 方法中,您执行 ToList 将所有人员带入内存,然后与 GetPersons1 方法相比,您在内存中过滤结果当过滤发生在数据库级别时,我的猜测是当您第二次调用 GetPerson2 方法时,因为您使用的是相同的上下文 EntityFramework 使用一些缓存机制来检索所有人员,这使得您正在过滤的列表受到您的更改的影响并且在此列表中没有任何年龄为 25 岁的人,这就是 p2_after 为空的原因。

为了确认或拒绝我的假设,我将尝试三种不同的场景:

  • 在两次调用之间保存对数据库的更改:
    public void OnGet()
    {
        Person p1 = GetPerson1();
        Person p2 = GetPerson2();
    
        p1.Age = 26;
        p2.Age = 26;
    
        _context.SaveChanges();
    
        Person p1_after = GetPerson1();
        Person p2_after = GetPerson2();
    }
    

    在这种情况下,我猜 p1_after 和 p2_after 将相同(均为空),因为现在您的更改也反射(reflect)到数据库中。
  • 为每个调用使用新的上下文:
    public void OnGet()
    {
        Person p1 = GetPerson1();
        Person p2 = GetPerson2();
    
        p1.Age = 26;
        p2.Age = 26;
    
        Person p1_after = GetPerson1();
        Person p2_after = GetPerson2();
    }
    
    public Person GetPerson1()
    {
        using(var context = new ...)
        {
            return context
               .Persons
               .Where(p => p.Age == 25)
               .SingleOrDefault();
        }
    }
    
    public Person GetPerson2()
    {
        using(var context = new ...)
        {
            return context
               .Persons
               .ToList()
               .Where(p => p.Age == 25)
               .SingleOrDefault();
        }
    }
    

    在这种情况下,我猜 p1_after 和 p2_after 将相同(与之前的 p1 和 p2 相同),因为现在您的更改不会反射(reflect)到数据库中,并且没有任何缓存可行性,因为您每次调用都使用新的上下文。
  • 使用 AsNoTracking:
    public void OnGet()
    {
        Person p1 = GetPerson1();
        Person p2 = GetPerson2();
    
        p1.Age = 26;
        p2.Age = 26;
    
        Person p1_after = GetPerson1();
        Person p2_after = GetPerson2();
    }
    
    public Person GetPerson1()
    {
        return _context
            .Persons
            .AsNoTracking()
            .Where(p => p.Age == 25)
            .SingleOrDefault();
    }
    
    public Person GetPerson2()
    {
        return _context
            .Persons
            .ToList()
            .AsNoTracking()
            .Where(p => p.Age == 25)
            .SingleOrDefault();
    }
    

    在这种情况下,我猜 p1_after 和 p2_after 将相同(与之前的 p1 和 p2 相同),因为现在 EF 跟踪被禁用 - 也就是没有缓存。
  • 10-07 20:04
    查看更多