我从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。
我认为没有办法解决它,除了知道它是如何工作的(并且因此尝试过滤另一个没有改变的属性以确保在过滤之前不将整个表拖到客户端) ?
答案 0 :(得分:3)
您使用的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)的对象,因为尚未使用{更改数据库与数据库同步{1}}。
SaveChanges
现在您有一张表中所有项目的列表。 var allItems = Foos.ToList();
合并选项执行以下操作:
AppendOnly
结论是:使用Id=1 - Materialize object
Id=2 - Materialize object
...
Id=5 - Already exists! Give the existing instance
...
选项时,有两种状态,即数据库状态和实体框架缓存状态。如果查询AppendOnly
列表,它将始终转到数据库,但返回的对象将通过主键值与已存在的值匹配。如果找到现有对象,则返回该实例。
这也解释了为什么你的第二种情况会返回有问题的对象,你首先检索整个表,然后过滤它。
如果没有更多背景,很难提出解决这个问题的建议。最有可能的是,你想早点保存。
答案 1 :(得分:1)
陈述......
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()
时,将从数据库中提取所有 foos。默认情况下,EF不会覆盖它已经跟踪的项目。毕竟,你可能会失去你所做的改变。因此,此语句会将新的foo项附加到Local
集合中,但尚未存在。