如何使用System.Data.Entity.Infrastructure.DbChangeTracker跟踪关联表变更
EF6 多对多隐藏联结表变更跟踪解决方案
问题根因:你当前用ChangeTracker.Entries()只能查询到被跟踪的实体本身的状态变更,而你配置的是无实体类的多对多关联,goodCombination是EF内部维护的隐藏联结表,关联的新增/删除属于「关系状态变更」,不属于实体本身的状态变更,所以不会出现在Entries()的返回结果中,这是EF6的默认设计。
以下是两种可行的实现方案:
方案1:直接查询EF内部维护的关系状态
不需要修改现有多对多映射,通过DbContext底层封装的ObjectContext即可获取关系对象的变更记录:
using System.Data.Entity.Infrastructure; using System.Data.Entity.Core.Objects; // 写在你的DbContext类内部 public List<ObjectStateEntry> GetChangedManyToManyRelations() { var objectContext = ((IObjectContextAdapter)this).ObjectContext; // 筛选出状态为新增/删除的多对多关系条目 return objectContext.ObjectStateManager .GetObjectStateEntries(EntityState.Added | EntityState.Deleted) .Where(e => e.IsRelationship) .ToList(); }
你可以遍历返回的关系条目,通过e.CurrentValues和e.OriginalValues获取关联两端的实体主键,判断是哪两个实体的关联发生了变更。
方案2:将多对多联结表显式映射为实体
如果需要更简单的跟踪逻辑,或者后续需要给goodCombination加额外字段(比如搭配评分、生效时间等),推荐把隐藏的联结表改成显式实体:
第一步:定义联结表实体
public class GoodCombination { public string BreadName { get; set; } public string ToppingName { get; set; } public virtual Bread Bread { get; set; } public virtual Topping Topping { get; set; } }
第二步:修改原实体的导航属性
public class Bread { public string Name { get; set; } public int TasteScore { get; set; } // 替换原来的ICollection<Topping> public virtual ICollection<GoodCombination> GoodCombinations { get; set; } } public class Topping { public string Name { get; set; } public bool ContainsLactose { get; set; } // 替换原来的ICollection<Bread> public virtual ICollection<GoodCombination> GoodCombinations { get; set; } }
第三步:Fluent API 配置联合主键和关联
在你的DbContext的OnModelCreating方法中添加配置:
modelBuilder.Entity<GoodCombination>() .HasKey(gc => new { gc.BreadName, gc.ToppingName }); modelBuilder.Entity<GoodCombination>() .HasRequired(gc => gc.Bread) .WithMany(b => b.GoodCombinations) .HasForeignKey(gc => gc.BreadName); modelBuilder.Entity<GoodCombination>() .HasRequired(gc => gc.Topping) .WithMany(t => t.GoodCombinations) .HasForeignKey(gc => gc.ToppingName);
配置完成后,关联关系的变更会对应GoodCombination实体的Added/Deleted状态,你原来的ChangeTracker.Entries()查询代码不需要改就能直接捕获到变更。
注意事项
- 所有导航属性必须声明为
virtual,确保EF的动态代理能正常工作,否则可能出现跟踪失效的问题 - 导航属性的修改必须在实体被当前DbContext跟踪的范围内操作,如果你是从外部传入detached状态的实体,需要先调用
Attach()附加到上下文再修改关联
内容的提问来源于stack exchange,提问作者CorBins
相关产品推荐
相关产品推荐

