C# EF 6.0 where条款后的不正确项目
在使用EF(FirstOrDefault)修改一个值并在另一个集合中使用Where子句请求相同的项之后,我对EF 6.0有一个问题 例如,假设类Foo具有3个属性:Id、Desc、StatusId,其中Foos是EntitySet。最初在第一次提取之后:C# EF 6.0 where条款后的不正确项目,c#,entity-framework,C#,Entity Framework,在使用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 fro
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
我认为,除了知道它是如何工作的(因此,尝试对另一个未更改的属性进行筛选,以确保在筛选之前不会将整个表拖到客户端)之外,没有其他方法可以解决此问题。发生的情况是,您正在使用的,很可能是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
merge选项执行以下操作:
Id=1 - Materialize object
Id=2 - Materialize object
...
Id=5 - Already exists! Give the existing instance
...
结论是:当使用AppendOnly
选项时,有两种状态:数据库状态和实体框架缓存状态。如果查询Foos
列表,它将始终转到数据库,但返回的对象将通过主键值与已经存在的对象相匹配。如果找到现有对象,将返回该实例
这也解释了为什么第二种情况会返回有问题的对象,首先检索整个表,然后过滤它
没有更多的背景,就很难提出解决这个问题的建议。最有可能的情况是,您希望提前保存。发生的情况是您正在使用的,最有可能的是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
merge选项执行以下操作:
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查询只返回此时数据库中状态ID==5的对象。对象项
尚未保存,因此未包括在内
因此,您在这里所做的是获取一个具有某些StatusId(可能不是5)的对象,将其更改为StatusId=5,然后获取更多已经具有StatusId=5的对象。上下文中的项数现在是最新查询中的对象数+1
可以强制where子句在DataContext中的附加对象上运行吗
(顺便说一句,DbContext)是的,通过查询本地集合:
Foos.Local.Where(f => f.StatusId == 5)
在这种情况下,此语句将返回到目前为止您在上下文中拥有的所有Foo
项
执行Foos.ToList()
时,将从数据库中获取所有foo。默认情况下,EF不会覆盖其已跟踪的项目。毕竟,你可能会失去你所做的改变。因此,此语句将向尚未存在的本地集合追加新的foo项。该语句
var statusItems = Foos.Where(f => f.StatusId == 5)
…总是转到数据库。SQL查询只返回此时数据库中状态ID==5的对象。对象项
尚未保存,因此未包括在内
因此,您在这里所做的是获取一个具有某些StatusId(可能不是5)的对象,将其更改为StatusId=5,然后获取更多已经具有StatusId=5的对象。上下文中的项数现在是最新查询中的对象数+1
可以强制where子句在DataContext中的附加对象上运行吗
(顺便说一句,DbContext)是的,通过查询本地集合:
Foos.Local.Where(f => f.StatusId == 5)
在这种情况下,此语句将返回到目前为止您在上下文中拥有的所有Foo
项
执行Foos.ToList()
时,将从数据库中获取所有foo。默认情况下,EF不会覆盖其已跟踪的项目。毕竟,你可能会失去你所做的改变。因此,此语句将把新的foo项附加到Local
集合中,而该集合中还没有这些项。我有一个具有2个PK键(Id和版本)的项,因此我使用它来筛选集合,然后它将找到正确的项。我只是想知道是否有其他方法可以绕过它,而不是保存或使用更大的收藏。尽管如此,你想得越多,它就越有意义