我有一张这样的桌子:
[Id] [INT] IDENTITY(1,1) NOT NULL,
..
..
[ParentId] [INT] NULL,
[CreatedOn] [DATETIME] NOT NULL,
[UpdatedOn] [DATETIME] NOT NULL
在某些情况下,我想用表的 Id 更新 ParentId,如下所示:
_dbContext.Add(data);
if (true)
{
data.ParentId = data.Id;
}
_dbContext.Update(data);
await _dbContext.SaveChangesAsync();
这样做时,我收到此错误:
<块引用>实体类型 'Data' 上的属性 'Id' 有一个临时值,而 试图将实体的状态更改为“已修改”。要么设置一个 显式永久值或确保数据库已配置 为该属性生成值。
是否有可能是我正在尝试执行的操作,或者我需要在更新之前先调用 SaveChangesAsync()?
答案 0 :(得分:0)
Entity Framework 有一个叫做更改跟踪器的东西。这将执行一些职责,我将提及与此答案相关的职责:
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
值,而这与您报告的错误消息无关。