修改实体时变更跟踪条目的状态相关技术咨询
嘿,我看你贴了一个Survey实体的代码,应该是在处理修改实体时EF Core变更跟踪状态的问题吧?先把你的实体代码完整梳理出来(修正了转义符号):
public class Survey : EntityBase { protected Survey() { } public Survey(long id, int version, string title, long userId, string phone) { Id = id; CreationTimeUtc = DateTimeOffset.UtcNow; Version = version; Title = title; UserId = userId; Phone = phone; Status = SurveyStatus.Initial; IsDeleted = false; Questions = new List<SurveyQuestion>(); Feedbacks = new List<SurveyFeedback>(); } public int Version { get; protected set; } public long UserId { get; protected set; } public string Phone { get; protected set; } public string Title { get; protected set; } public SurveyStatus Status { get; protected set; } public bool IsDeleted { get; protected set; } public ICollection<SurveyQuestion> Questions { get; protected set; } public ICollection<SurveyFeedback> Feedbacks { get; protected set; } }
从代码能看出来,你用了**领域驱动设计(DDD)**的封装风格,所有属性的setter都是protected,目的是让实体的状态只能通过自身的方法来修改,这是非常好的实践,但确实会和EF Core的变更跟踪机制产生一些需要注意的点,我来给你梳理下常见的场景和解决方法:
场景1:实体处于上下文跟踪状态下修改
如果你是从DbContext查询出Survey实体后,直接通过实体内部的方法修改状态(比如给Survey加个修改状态的方法),EF Core的变更跟踪会自动感知到这些变化。举个例子,给Survey加一个合规的状态修改方法:public void MarkAsCompleted() { if (Status != SurveyStatus.Initial) { throw new InvalidOperationException("只有初始状态的问卷才能标记为完成"); } Status = SurveyStatus.Completed; Version++; // 版本号自增用于乐观并发控制 }当你调用
survey.MarkAsCompleted()后,只要这个survey对象还被DbContext跟踪,EF Core会在你调用SaveChanges()时自动检测到Status和Version属性的变化,生成对应的UPDATE语句。场景2:离线实体重新附加后的修改
如果你的Survey实体是脱离DbContext的(比如从前端接收的DTO转换而来,或者之前查询后序列化过),重新附加到DbContext时,需要手动告诉EF Core哪些属性发生了变化:- 如果你不确定哪些属性变了,可以直接标记整个实体为Modified:
context.Surveys.Update(survey); - 如果你知道具体修改的属性,可以更精细地标记,避免不必要的字段更新:
context.Surveys.Attach(survey); context.Entry(survey).Property(s => s.Status).IsModified = true; context.Entry(survey).Property(s => s.Version).IsModified = true;
- 如果你不确定哪些属性变了,可以直接标记整个实体为Modified:
关键配置提示
因为你的属性setter是protected,EF Core默认是可以识别这些属性并进行变更跟踪的,但如果你的实体是用私有字段来存储状态(比如private SurveyStatus _status;,然后属性是public SurveyStatus Status => _status;),那你需要在DbContext的OnModelCreating里显式配置字段映射:modelBuilder.Entity<Survey>() .Property(s => s.Status) .HasField("_status");这样EF Core就能直接访问私有字段来跟踪变化了。
备注:内容来源于stack exchange,提问作者Mofid.Moghimi

