Change Tracker 通过记录实体的状态来跟踪对象的变化。当你对一个实体(如一个 User
对象)进行修改时,Entity Framework 会跟踪该实体的状态,直到调用 SaveChanges()
时才将这些更改同步到数据库。
Entity Framework 中的实体有以下几种可能的状态,Change Tracker 负责管理这些状态:
-
Added(新增):实体是新创建的,并且还没有保存到数据库中。
- 例如,在
context.Users.Add(new User())
后,User
实体的状态会是Added
。
- 例如,在
-
Modified(修改):实体已经存在于数据库中,并且其属性值被修改过。
- 例如,
user.Name = "new name"
后,user
实体的状态会是Modified
。
- 例如,
-
Deleted(删除):实体被标记为删除,表示要从数据库中移除。
- 例如,
context.Users.Remove(user)
后,user
实体的状态会是Deleted
。
- 例如,
-
Unchanged(未变更):实体没有做任何修改,它的值与数据库中的记录相同。
- 例如,在查询数据库并没有修改实体后,实体状态会是
Unchanged
。
- 例如,在查询数据库并没有修改实体后,实体状态会是
-
Detached(分离):实体不再与任何
DbContext
实例相关联。它不参与 Change Tracker 的跟踪。- 例如,实体从上下文中移除(
context.Entry(user).State = EntityState.Detached
)或不在上下文管理的情况下。
- 例如,实体从上下文中移除(
// 检查用户的当前状态
var entry = context.Entry(user);
Console.WriteLine(entry.State); // 输出: Modified
Change Tracker 底层架构
-
DbContext:
-
DbContext
是 Entity Framework 与数据库交互的主要类。 -
它包含一个
ChangeTracker
属性,该属性可以用于访问和操作跟踪的实体。
-
-
ChangeTracker:
-
ChangeTracker
是一个内部对象,负责管理实体的状态(如新增、修改、删除和未更改的状态)。 -
它通过跟踪
DbContext
中的实体的状态来进行工作。
-
-
DbEntityEntry:
-
DbEntityEntry
是用于访问单个实体状态的核心类。它提供了方法来获取和设置实体的状态,管理实体的属性值,甚至可以进行深度比较(例如,检测修改的字段)。
-
底层工作原理
ChangeTracker
会在 DbContext
中维护一组被跟踪的实体及其状态。它会根据实体的状态(例如,Added
、Modified
、Deleted
等)来确定数据库操作的类型,并生成相应的 SQL 操作。在执行 SaveChanges()
时,ChangeTracker
会识别这些变化并生成相应的 SQL 语句。
ChangeTracker 和 DbContext
DbContext
在内存中维护了一个包含所有跟踪实体的列表。这些实体会被 ChangeTracker
追踪,当你对实体执行操作时(如增加、修改或删除),ChangeTracker
会记录它们的状态。
-
当一个实体被添加到
DbContext
中时,它的状态会被标记为Added
。 -
如果实体的属性发生变化,
ChangeTracker
会将其状态标记为Modified
。 -
如果你调用
Remove()
删除一个实体,它的状态将被标记为Deleted
。
DbContext 和 Entity Entry
当你通过 DbContext.Entry(entity)
访问实体时,实际上是获取了该实体的 DbEntityEntry
对象。DbEntityEntry
提供了对实体的状态、属性值和其他元数据的访问。
简化实现
// 自定义的数据库上下文类,模拟了 Entity Framework 的行为
public class MyOwnDatabase
{// 用于存储被跟踪的实体的列表public List<EntityEntry> TrackedEntities { get; } = new List<EntityEntry>();// 向数据库添加一个实体(模拟 Entity Framework 中的 Add 方法)public void Add(object entity){// 创建一个新的 EntityEntry 实例,表示该实体的状态为 "Added"(新增)var entry = new EntityEntry(entity, EntityState.Added);// 将该实体的 Entry 添加到 TrackedEntities 列表中进行跟踪TrackedEntities.Add(entry);}// 从数据库删除一个实体(模拟 Entity Framework 中的 Remove 方法)public void Remove(object entity){// 创建一个新的 EntityEntry 实例,表示该实体的状态为 "Deleted"(删除)var entry = new EntityEntry(entity, EntityState.Deleted);// 将该实体的 Entry 添加到 TrackedEntities 列表中进行跟踪TrackedEntities.Add(entry);}// 获取一个实体的 Entry,用于查看或修改实体的状态public EntityEntry Entry(object entity){// 返回 TrackedEntities 中的匹配实体 Entry(如果有的话)return TrackedEntities.FirstOrDefault(e => e.Entity == entity);}// 保存更改到数据库(模拟 Entity Framework 中的 SaveChanges 方法)public void SaveChanges(){// 遍历所有被跟踪的实体,执行相应的数据库操作foreach (var entry in TrackedEntities){switch (entry.State){case EntityState.Added: // 新增状态:执行插入操作Insert(entry);break;case EntityState.Modified: // 修改状态:执行更新操作Update(entry);break;case EntityState.Deleted: // 删除状态:执行删除操作Delete(entry);break;}}}// 执行插入操作,模拟 INSERT SQL 语句的执行private void Insert(EntityEntry entry){// 输出模拟插入操作的消息Console.WriteLine($"Executing INSERT for {entry.Entity.GetType().Name}");}// 执行更新操作,模拟 UPDATE SQL 语句的执行private void Update(EntityEntry entry){// 输出模拟更新操作的消息Console.WriteLine($"Executing UPDATE for {entry.Entity.GetType().Name}");}// 执行删除操作,模拟 DELETE SQL 语句的执行private void Delete(EntityEntry entry){// 输出模拟删除操作的消息Console.WriteLine($"Executing DELETE for {entry.Entity.GetType().Name}");}
}// 表示被跟踪的实体及其状态的类
public class EntityEntry
{// 被跟踪的实体public object Entity { get; }// 实体的当前状态(新增、修改、删除、未更改)public EntityState State { get; set; }// 构造函数,接受实体和其状态作为参数public EntityEntry(object entity, EntityState state){Entity = entity; // 设置实体State = state; // 设置实体的状态}
}// 定义实体的状态枚举
public enum EntityState
{Added, // 实体已添加(新增)Modified, // 实体已修改Deleted, // 实体已删除Unchanged // 实体未发生变化
}