您遇到的错误
Entity Framework 有一个叫做 change tracker 的东西。这执行了一些职责,我将提到与此答案相关的职责:
- 当您致电
SaveChanges() 时,它会跟踪需要发生的事情
- 它会密切关注所有连接的实体
当您调用SaveChanges() 时,EF 可能需要INSERT 一些实体,它需要UPDATE 其他一些(我忽略了其他操作,因为它们在这里无关紧要。为了跟踪这一点,EF 的更改跟踪器将一个名为 EntityState 的特定枚举附加到您的实体。根据您调用的方法,设置枚举值。
_dbContext.Add(data);
var the_enum_value = _dbContext.Entry(data).State;
您会看到the_enum_value 等于EntityState.Added。这意味着当你调用SaveChanges()时,会生成一个INSERT语句。
_dbContext.Update(data); //let's assume this is an existing entity you fetched
var the_enum_value = _dbContext.Entry(data).State;
在这里,您将看到 the_enum_value 等于 EntityState.Modified。这意味着当你调用SaveChanges()时,会生成一条UPDATE语句。
您添加了一个新实体,因此您显然希望将其插入到数据库中。但是通过在该实体上调用 Update,您会将枚举值更改为 EntityState.Modified,从而导致生成 UPDATE 语句,即使数据库中还没有您需要更新的现有行。
在过去,EF 只是更改了枚举,无论如何都尝试过,然后你会摸不着头脑,为什么你的更改没有进入数据库。
如今,EF 已经足够聪明地意识到,如果实体还没有实际的 ID 值,那么将枚举设置为 EntityState.Modified 将是一个坏主意,因此它会提醒您您正在尝试做一些不起作用的事情。
这里的一般解决方案是您根本不需要致电Update。你可以这样做:
var data = new Data() { Name = "Foo" };
_dbContext.Add(data);
data.Name = "Bar";
await _dbContext.SaveChangesAsync();
您将看到访问数据库的名称是“Bar”,而不是“Foo”。因为EF只在你调用SaveChanges的时候生成INSERT语句,而不是在你调用Add的时候;因此,在调用 SaveChanges 之前所做的任何更改仍会被正在生成的 INSERT 语句“看到”。
你正在尝试什么
但是,您处于一种特别特殊的情况,因为您正在尝试访问和使用要创建的实体的 ID 属性。该值尚不存在。
EF 尽最大努力忽略这一点,并使其在幕后为您服务。当您调用 SaveChanges() 时,EF 会找出生成的 ID 是什么,并默默地为您填写,这样您就可以继续使用您的 data 变量而无需担心。
但我非常怀疑 EF 是否能够意识到data.ParentId 需要同样的处理。
更新前需要先调用 SaveChangesAsync() 吗?
在这种非常具体的情况下,很可能是的。但是,这是因为您尝试使用 data.Id 值,这与您报告的错误消息无关。