Core EF 实体跟踪

最近一段时间在学习Core EF,我根据网上教程,和经过相关测试,总结了一些有关于Core EF变更跟踪器ChangeTracker的状态特性,主要目的是做记录,避免遗忘一些细节,仅供参考。

先贴上大佬有关ChangeTracker比较简短的回答

 

 

Core EF 实体的5种状态。

 
 
 
 
 
 
 
 
 
 
 
 
  • Detached 实体未被 DbContext 跟踪。
  • Added 实体是新实体,并且尚未插入到数据库中。 这意味着它们将在调用 SaveChanges 时插入。
  • Unchanged 实体自从数据库中查询以来尚未进行更改。 从查询返回的所有实体最初都处于此状态。
  • Modified 实体自从数据库中查询以来已进行更改。 这意味着它们将在调用 SaveChanges 时更新。
  • Deleted 实体存在于数据库中,但标记为在调用 SaveChanges 时删除。

 

DbContext 方法

Add 方法添加新实体,将实体状态设置为:Added。

  • 导航内现有的实体:
  • 注意只有主实体未被跟踪(Detached状态)才会变更导航实体状态
  1. 原状态为Detached时,修改外键,变更状态为 Added。
  2. 原状态为Unchanged时,修改外键,变更状态为 Modified。
  3. 原状态为Modified时,修改外键,状态不变。
  4. 原状态为Deleted时,修改外键,状态不变。
  5. 原状态为Added时,修改外键,状态不变。
 

 

Remove方法删除实体,将实体状态设置为:Deleted。

  • 导航内现有的实体:
  • 注意只有主实体未被跟踪(Detached状态)才会变更导航实体状态,其中如果主实体状态是Added,调用该方法会直接将其状态变更为Detached(取消跟踪)
  1. 设置了联级删除,Cascade,ClientCascade ,变更状态为 Deleted。
  2. 设置了联级 SetNull,ClientSetNull,修改外键为null,设置状态为:Modified。
  3. 不管导航是否是跟踪状态,是否存在从属关系,只要主键在数据库中实际存在,都对其标记为删除或修改外键的状态。

 

Attach方法附加实体,将实体状态设置为:Unchanged。(所有属性IsModified=false)

· Attach方法会将跟踪器中的OriginalValue 原始值重置为修改值,这正好与直接设置EntityEntry.State = EntityState.Unchanged 时会将修改值重置为原始值的特性相反。

· Attach方法对于包含生成的键的实体类型,如果实体设置了其主键值,则会在状态中跟踪该实体Unchanged。 如果未设置主键值,则会在状态中跟踪Added。(导航内实体也符合该特性)

  • 导航内现有的实体:
  • 注意只有主实体未被跟踪(Detached状态)才会变更导航实体状态
  1. 原状态为Detached时,修改外键,变更状态为 Unchanged(有主键)。
  2. 原状态为Unchanged时,修改外键,变更状态为 Modified。
  3. 原状态为Modified时,修改外键,状态不变。
  4. 原状态为Deleted时,修改外键,状态不变。
  5. 原状态为Added时,修改外键,状态不变。

 


Update方法更新实体,将实体状态设置为:Modified(所有属性IsModified=true)

· 对于包含生成的键的实体类型,如果实体设置了其主键值,则会在状态中跟踪该实体Modified。 如果未设置主键值,则会在状态中跟踪Added

  • 导航内现有的实体:
  • 注意只有主实体未被跟踪(Detached状态)才会变更导航实体状态
  1. 原状态为Detached时,修改外键,变更状态为 Modified(所有属性IsModified=true)
  2. 原状态为Unchanged时,修改外键,变更状态为 Modified(其他属性IsModified不变)
  3. 原状态为Modified时,修改外键,状态不变。(其他属性IsModified不变)
  4. 原状态为Deleted时,修改外键,状态不变。
  5. 原状态为Added时,修改外键,状态不变。

 

· 注意,导航内现有的实体时指:在Attach或Update之前,导航属性中就已经存在的实体,而Attach后由于EF跟踪机制,自动关联的已加载实体不属于现有的。

 

自动检测更改的方法

  • 在EF中,修改属性值后,最终需要通过调用DetectChanges(),比对OriginalValue来检测值是否更改过,从而设置对应的 EntityState 状态以及属性的 IsModified

在一些方法中会自动调用上下文中的ChangeTracker.DetectChanges

 

在某些位置,只会对单个实体实例进行更改检测,而不是在整个所跟踪实体关系图上发生更 改。 这些位置包括:

  • 使用时 DbContext.Entry ,确保实体的状态和修改的属性是最新的。
  • 使用 EntityEntry 、以确保调用 PropertyCollectionReferenceMember 等方法时,属性的修改、当前值等是最新的。
  • 当要删除依赖/子实体时,因为所需的关系已被断开。 这会检测不应删除实体的时间,因为它已重新获得父级。

补充:DbContext.Entry 时会调用单EntityEntry实例中DetectChanges,调用 Property,Collection,Reference,Member 等方法则不会。

可以通过设置 ChangeTracker.AutoDetectChangesEnabled 来关闭DetectChanges的自动调用。

 

指定属性为更新状态

EntityEntry实例中通过调用Property方法.IsModified 来获取属性的修改状态。

也可以设置该属性,将其标识为已更新。我们前面提到过:EntityEntry.State = EntityState.Unchanged 会将修改值重置为原始值,而将IsModified设置为false,也会有一样的效果。

当我们将任意属性的 IsModified 设置为true,被跟踪的实体状态将会被变更为 Modified。我们将所有属性的IsModified 设置为false,被跟踪的实体状态将会被变更为 Unchanged。

 

直接简单的设置EntityState

通过直接设置 EntityEntry.EntityState 状态,来影响SaveChanges时生成的SQL。与Add,Remove,Attach,Update 等方法相比,直接设置EntityState不会对导航属性内现有的实体的状态进行变更。

 

DetectChanges 方法应是检测实体:状态为Unchanged 的所有属性的更改情况,和状态为Modified,属性IsModified为false的更改情况。

DetectChanges 针对实体内的导航实体状态为Detached:

  1. 自动生成主键的实体,有主键状态变更为:Modified(关系修改)
  2. 自动生成主键的实体,无主键状态变更为:Added(关系绑定)
  3. 常规主键的实体,有主键状态变更为:Added(关系绑定)
  4. 常规主键的实体,无主键抛出异常

DetectChanges 针对实体内的导航实体是被踪的状态的。仅检测关系变更(外键)。

但是导航实体作为被跟踪的实体,也会被DetectChanges 单独检测。

 

随笔移植,原文地址:https://zhuanlan.zhihu.com/p/419496293

 

posted @ 2022-05-24 19:29  状态的状  阅读(336)  评论(0编辑  收藏  举报