下面是我的代码的简化版本。我希望p1
和p2
相等,p1_after
和p2_after
相等,因为GetPerson1()
和GetPerson2()
之间的唯一区别是强制执行查询的.ToList()
子句,而不对选择标准进行任何更改。
我的假设不正确吗?我在程序中发现了一个错误,该错误归结为p1_after
并且p2_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
方法,因此您的更改不会反映在数据库中,而只会反映到您的代码中。
现在,当您再次调用该方法时GetPerson1
您要求从数据库中获取年龄为25岁的人,并且由于数据库未反映在您的更改中,因此您将获得与以前相同的结果。
奇怪的部分是为什么当你调用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将是相同的(都是空的),因为现在您的更改也会反映到数据库中。
-
为每个调用使用新的上下文:
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 一样),因为现在您的更改不会反映到数据库中,并且没有任何缓存可行性,因为您每次调用都使用新的上下文。
-
使用 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 跟踪被禁用 - AKA 没有缓存。