EF Core 7替换Json存储的自有实体报错,该行为是否正常?
问题背景
我正在使用EF Core 7,通过以下配置将集合以JSON格式存储为实体表列:
builder.Entity<ShoppingChannels>().OwnsMany(s => s.ChannelProducts, options => { options.ToJson(); });
实体类定义如下:
public class ShoppingChannels { [Key] public string Id { get; set; } public List<ChannelProducts> ChannelProducts { get; set; } } public class ChannelProducts { public string ProductId { get; set; } public string Title { get; set; } = string.Empty; public double Price { get; set; } }
我尝试通过替换集合的方式限制JSON元素数量为50:
using (var context = new DbContext()) { var shoppingChannelsList = await context.ShoppingChannels .ToListAsync(); foreach (var channel in shoppingChannelsList) { var filteredChannelProducts = channel.ChannelProducts .Take(50) .ToList(); channel.ChannelProducts = filteredChannelProducts; } await context.SaveChangesAsync(); }
执行SaveChanges时抛出异常:
System.InvalidOperationException: 'The instance of entity type 'ChannelProducts' cannot be tracked because another instance with the key value '{ShoppingChannelsId: fc470701-f01a-4f6b-b424-379edb315ada, Id: 1}' is already being tracked. When replacing owned entities, modify the properties without changing the instance or detach the previous owned entity entry first.'
改为逐个删除超出50的元素后代码可正常运行:
using (var context = new DbContext()) { var shoppingChannelsList = await context.ShoppingChannels .ToListAsync(); foreach (var channel in shoppingChannelsList) { var productsToRemove = channel.ChannelProducts .Skip(50) .ToList(); foreach (var product in productsToRemove) { channel.ChannelProducts.Remove(product); } } await context.SaveChangesAsync(); }
请问这是EF Core 7的正常行为,还是我在此场景中遗漏了某些逻辑?
解答
这是EF Core 7的正常行为,核心原因是EF Core对ToJson()存储的Owned集合的跟踪机制限制:
- Owned实体的跟踪逻辑:加载
ShoppingChannels时,EF Core会自动跟踪所有关联的ChannelProducts实例,每个Owned实体都带有隐式复合键(包含主实体ID和自身标识),这些实例会被上下文的变更追踪器记录。 - 直接替换集合的冲突:当你直接赋值新集合
channel.ChannelProducts = filteredChannelProducts时,原集合中被跟踪的ChannelProducts实例并未被上下文解除跟踪。新集合中的实例和原跟踪实例的复合键完全一致,导致变更追踪器触发"重复跟踪"的异常。 - 逐个删除的正确性:通过
Remove方法修改原集合时,EF Core会准确识别出被移除的元素,更新变更追踪器的状态,最终将这些变更转换为JSON列的更新操作,不会产生跟踪冲突。
总结:对于EF Core的Owned集合(尤其是JSON存储的类型),必须通过修改现有集合的内容(添加/删除元素)来变更数据,而非直接替换集合引用。
内容的提问来源于stack exchange,提问作者bob mason
相关产品推荐
相关产品推荐

