EF Core 8 + PostgreSQL JSON列变更跟踪失效问题排查
EF Core 8 + PostgreSQL JSON列变更跟踪问题:标记Modified后无法保存变更
模型定义
public class DocumentReadModelEntity<T> where T : IDocumentReadModel { public Guid Id { get; set; } public T Data { get; set; } public DocumentReadModelEntity() { } public DocumentReadModelEntity(T data) { Data = data; Id = data.Id; } }
public sealed class SupplierReadModel : IDocumentReadModel { public string? Description { get; set; } public string? Website { get; set; } public Guid? LogoFileId { get; set; } public bool IsActive { get; set; } public bool ShouldSendRemittanceEmail { get; set; } public List<SupplierBlockModel> Blocks { get; set; } public List<EstateBlockModel> Estates { get; set; } public Guid CompanyId { get; set; } public CompanyShortData? CompanyData { get; set; } public BankAccountModel? BankAccount { get; set; } }
SupplierReadModel中的复杂属性均继承自标记了[Owned]的抽象类ReadModelDataModel,示例:
public class SupplierBlockModel : ReadModelDataModel { public Guid BlockId { get; set; } public BlockShortData? BlockData { get; set; } }
实体配置
public static void ApplyDefaultConfiguration<T>(this EntityTypeBuilder<DocumentReadModelEntity<T>> builder) where T : class, IDocumentReadModel { builder.ToTable(typeof(T).Name); builder.HasKey(x => x.Id); builder.OwnsOne(x => x.Data, x => { x.ToJson(); }); }
查询与更新逻辑
查询时使用AsNoTracking直接获取Data属性:
return await _entities.AsNoTracking() .Where(x => ids.Contains(x.Id)) .Select(x => x.Data) .ToListAsync();
修改SupplierReadModel后,通过创建新实体并标记状态来更新:
public void Update(T updated) { var entity = new DocumentReadModelEntity<T>(updated); _entities.Entry(entity).State = EntityState.Modified; }
问题现象
调用SaveChangesAsync()后无任何数据写入数据库,查看DbContext变更跟踪器调试视图显示:
DocumentReadModelEntity<SupplierReadModel> {Id: 018d834f-75d9-48eb-8af1-543579976bc5} Modified Id: '018d834f-75d9-48eb-8af1-543579976bc5' PK Data: <not found>
问题原因
- 新创建的
DocumentReadModelEntity实例中,Data作为Owned JSON属性未被EF Core变更跟踪器正确识别为已变更属性。仅标记整个实体为Modified时,EF无法感知Data的具体变化,调试视图的<not found>说明跟踪器未加载该属性的当前值,因此不会生成JSON列的更新SQL。 - 之前用
AsNoTracking仅查询Data属性而非完整实体,导致变更跟踪器没有原始实体上下文可用于对比变更。
解决方案
方案1:显式标记Data属性为已修改
创建实体后先Attach到上下文,再单独标记Data属性为已修改:
public void Update(T updated) { var entity = new DocumentReadModelEntity<T>(updated); _entities.Attach(entity); // 显式标记Data属性为已变更 _entities.Entry(entity).Reference(e => e.Data).IsModified = true; }
方案2:查询完整实体并更新(推荐)
避免直接查询Data,而是查询完整的DocumentReadModelEntity(不使用AsNoTracking),更新Data属性后让EF自动跟踪变更:
// 查询完整实体(不带AsNoTracking) var entity = await _entities.FirstAsync(x => x.Id == updated.Id); // 更新Data属性 entity.Data = updated; // 无需手动设置状态,EF会自动标记Data为已修改
方案3:使用CurrentValues.SetValues设置属性
通过CurrentValues.SetValues直接设置Data属性的值,让EF识别变更:
public void Update(T updated) { // 先创建仅包含Id的实体并Attach var entity = new DocumentReadModelEntity<T> { Id = updated.Id }; _entities.Attach(entity); // 设置Data属性的当前值 _entities.Entry(entity).CurrentValues.SetValues(new { Data = updated }); }
内容的提问来源于stack exchange,提问作者bartosz.grzeda
相关产品推荐
相关产品推荐

