实体框架核心 - 使用单个 SaveChangesAsync()

时间:2021-04-20 10:09:28

标签: asp.net entity-framework entity-framework-core entity-framework-6

我有一张这样的桌子:

[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()?

1 个答案:

答案 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 值,而这与您报告的错误消息无关。

相关问题