EF 6.0 where子句后面的项目不正确

本文关键字:项目 不正确 子句 where EF | 更新日期: 2023-09-27 18:25:51

在EF(FirstOrDefault)修改一个值,然后用Where子句在另一个集合中请求相同的项后,我对EF 6.0提出了一个问题。

例如,假设类Foo具有3个属性:Id、Desc、StatusId,其中Foos是EntitySet。最初在第一次提取后:

var item = Foos.FirstOrDefault(f => f.Id = 5);
// item values are: Id:5, Desc:"Whatever", StatusId:1 after fetching from database
// Change statusId
item.StatusId = 5
// statusItems will not contain above object HOWEVER...
var statusItems = Foos.Where(f => f.StatusId == 5).ToList();
// allItems will contain item, with StatusId = 5
var allItems = Foos.ToList() // Or FirstOrDefault

EF是否会出现这种行为?如果是这样,是否可以强制where子句在DbContext中的附加对象上运行,而不首先指定.ToList()?

我为上述问题找到的一个可能的解决方案是:

var item = Foos.FirstOrDefault(f => f.Id = 5);
// item value are: Id:5, Desc:"Whatever", StatusId: 1 after fetching from database
// Change statusId
item.StatusId = 5
// statusItems will now contain item
var statusItems = Foos.ToList().Where(f => f.StatusId == 5).ToList();

绕过它的另一种方法是将它封装在我假设的事务中,并在修改StatusId属性后调用SaveChanges。

我认为除了知道它是如何工作的(因此,尝试对另一个没有更改的属性进行筛选,以确保在筛选之前不会将整个表拖到客户端)之外,没有其他方法可以绕过它?

EF 6.0 where子句后面的项目不正确

发生的情况是,您正在使用的MergeOption,很可能是AppendOnly(默认值),具有特定的行为。实体框架保存一个已在内存中具体化的对象列表。为了说明正在发生的事情:

var item = Foos.FirstOrDefault(f => f.Id = 5);

实体框架现在在内存中有(Id=5)

var statusItems = Foos.Where(f => f.StatusId == 5).ToList();

所有项目都是从数据库中检索的,数据库中的StatusId=5这不包括Id为5的对象,因为更改尚未使用SaveChanges与数据库同步。

var allItems = Foos.ToList();

现在您有了表中所有项目的列表。AppendOnly合并选项执行以下操作:

Id=1 - Materialize object
Id=2 - Materialize object
...
Id=5 - Already exists! Give the existing instance
...    

结论是:当使用AppendOnly选项时,有两种状态,数据库状态和实体框架缓存状态。如果查询Foos列表,它将始终转到数据库,但返回的对象通过主键值与现有对象相匹配。如果找到现有对象,则会返回该实例。

这也解释了为什么你的第二种情况会返回有问题的对象,你首先检索整个表,然后过滤它

如果没有更多的背景,就很难提出解决这个问题的建议。最有可能的情况是,您希望早点保存。

语句。。。

var statusItems = Foos.Where(f => f.StatusId == 5)

总是转到数据库。SQL查询只返回此时数据库中具有StatusId == 5的对象。对象item尚未保存,因此它不包括在内。

因此,您在这里所做的是获取一个具有StatusId(可能不是5)的对象,将其更改为StatusId=5,然后获取更多已经具有StatusId=5的对象。上下文中的项数现在是最新查询中的对象数+1。

你能强制where子句在DataContext中的附加对象上运行吗?

(顺便说一句,DbContext)是的,通过查询本地集合:

Foos.Local.Where(f => f.StatusId == 5)

在这种情况下,此语句将返回到目前为止您在上下文中拥有的所有Foo项。

执行Foos.ToList()时,所有foo都将从数据库中提取。默认情况下,EF不会覆盖它已经在跟踪的项目。毕竟,你可能会失去你所做的改变。因此,该语句将把新的foo项附加到Local集合中,而这些项还没有出现。