简体   繁体   English

EF 6.0 where子句后面的错误项目

[英]EF 6.0 incorrect items after where clause

I had a question about EF 6.0 after fetching an item from a DB with EF (FirstOrDefault) modifying a value and then asking for the same item in another collection with a Where clause. 在使用EF(FirstOrDefault)修改值从数据库中获取项目,然后使用Where子句在另一个集合中请求同一项目后,我遇到了关于EF 6.0的问题。

For instance assume the class Foo with 3 properties: Id, Desc, StatusId where Foos is the EntitySet. 例如,假设类Foo具有3个属性:Id,Desc,StatusId,其中Foos是EntitySet。 Initially after the first fetch: 最初在第一次获取后:

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

Is this behaviour to be expected from EF? EF会期望这种行为吗? If so, can you force the where clause to run over the attached objects in the DbContext without specifying .ToList() first? 如果是这样,是否可以在不先指定.ToList()的情况下强制where子句在DbContext中的附加对象上运行?

A possible fix I found for the above is: 我发现上述情况的一种可能的解决方法是:

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();

Another way to get around it is to wrap it in a Transaction I assume and calling SaveChanges after modifying the StatusId property. 解决该问题的另一种方法是将其包装在我假定的Transaction中,并在修改StatusId属性后调用SaveChanges。

I take it there is no way around it, apart from knowing how it works (and as such try to filter on another property that did not change to make sure you don't drag the entire table to the client before filtering)? 我认为除了了解它是如何工作以外,别无他法(因此尝试对未更改的其他属性进行过滤,以确保在过滤之前不会将整个表拖到客户端)?

What happens is the MergeOption you are using, most likely AppendOnly (the default value), has specific behavior. 会发生什么事是MergeOption您正在使用,最有可能的AppendOnly (默认值),具有特定的行为。 Entity Framework holds a list of objects already materialized in memory. 实体框架保存了已在内存中实现的对象列表。 To illustrate what is happening: 为了说明正在发生的事情:

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

Entity framework now has (Id=5) in memory 实体框架现在在内存中具有(Id = 5)

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

All items are retrieved from the database that have StatusId = 5 inside the database! 从数据库中检索到数据库内部具有StatusId = 5的所有项目 This does not include the object with (Id=5) because changes have not been synchronized with the database yet using SaveChanges . 这不包括具有(Id = 5)的对象,因为尚未使用SaveChanges将更改与数据库同步。

var allItems = Foos.ToList();

Now you have a list of all items from the table. 现在,您有了表格中所有项目的列表。 The AppendOnly merge option does the following: AppendOnly合并选项执行以下操作:

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

The conclusion is: when using the AppendOnly option there are two states, the database state and the entity framework cache state. 结论是:使用AppendOnly选项时,有两种状态,即数据库状态和实体框架缓存状态。 If you query the Foos list it will always go to the database, but the returned objects are matched by primary key value to what already exists. 如果您查询Foos列表,它将始终进入数据库,但是返回的对象将通过主键值与已经存在的对象进行匹配。 If an existing object is found, that instance will be returned. 如果找到了现有对象,则将返回该实例。

That also explains why your second situation will return the object in question, you first retrieve the whole table, then filter it. 这也解释了为什么第二种情况将返回有问题的对象,首先检索整个表,然后过滤它。

Without more context, it is hard to make a suggestion for solving this issue. 没有更多的背景信息,很难提出解决此问题的建议。 Most likely, you want to save earlier. 您最有可能想要更早保存。

The statement... 该声明...

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

...always goes to the database. ...总是去数据库。 The SQL query only returns object that have StatusId == 5 in the database at that moment. SQL查询此时仅返回数据库中StatusId == 5对象。 The object item has not been saved yet, so it is not included. 该对象item尚未保存,因此不包括在内。

So what you did here was fetch an object having some StatusId (probably not 5), changed it to StatusId = 5 and then fetched more object that already had StatusId = 5. The number of items in the context is now the number of objects from the latest query + 1. 因此,您在这里执行的操作是获取一个具有一些StatusId(可能不是5)的对象,将其更改为StatusId = 5,然后再获取更多已具有StatusId = 5的对象。上下文中的项目数现在是来自最新查询+1。

can you force the where clause to run over the attached objects in the DataContext? 您可以强制where子句在DataContext中的附加对象上运行吗?

(DbContext, by the way) Yes, by querying the local collection: (顺便说一下,DbContext)是的,通过查询本地集合:

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

In this situation, this statement will return all Foo items you have in the context so far. 在这种情况下,此语句将返回您到目前为止在上下文中拥有的所有Foo项。

When you do Foos.ToList() , all foos will be fetched from the database. 当您执行Foos.ToList() ,将从数据库中获取所有 foo。 By default, EF will not overwrite items it's already tracking. 默认情况下,EF不会覆盖已在跟踪的项目。 After all, you might lose changed you made. 毕竟,您可能会丢失所做的更改。 So this statement will append new foo items to the Local collection that were not there yet. 因此,该语句会将尚未存在的新foo项目附加到Local集合中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM