C# 向列表添加对象前更新时同id对象同步修改该如何解决
问题原因
EF Core的DbContext默认开启实体跟踪机制:在同一个_context实例的生命周期内,只要是通过该上下文查询得到的实体,相同主键ID的实体始终会指向同一个内存引用对象。你多次查询同ID的Item,实际拿到的都是同一个对象的引用,所以修改属性后,列表中之前插入的同ID对象自然会同步更新。
另外你代码中Item newItem = new Item();是无效代码,下一行查询结果直接覆盖了该变量,刚创建的新对象会被GC回收。
解决方案
- 方案1:查询时关闭跟踪(推荐无后续入库需求的场景)
查询时添加AsNoTracking()方法,上下文不会缓存跟踪返回的实体,每次查询都会生成新的对象实例。
修改后的代码如下:
List<Item> lst = new List<Item>(); foreach (Item currItemB in itemsB) { Item newItem = await _context.Item.AsNoTracking() .Where(x => x.id == currItemB.id) .FirstOrDefaultAsync(); newItem.param = currItemB.param; lst.Add(newItem); }
注意:该方案返回的实体不在上下文跟踪范围内,如果后续需要将实体更新回数据库,需要手动调用_context.Update(newItem)附加后再保存。
- 方案2:手动映射新实体(最稳妥可控)
不要直接将查询到的跟踪实体赋值给最终添加到列表的变量,自己手动新建Item实例,拷贝查询结果的属性值,从根源上保证实例独立。
代码示例:
List<Item> lst = new List<Item>(); foreach (Item currItemB in itemsB) { Item dbItem = await _context.Item .Where(x => x.id == currItemB.id) .FirstOrDefaultAsync(); // 手动拷贝所有需要的属性 Item newItem = new Item() { id = dbItem.id, // 其他属性依次赋值 param = currItemB.param }; lst.Add(newItem); }
- 方案3:深拷贝实体
如果实体属性较多不想手动赋值,可以通过深拷贝的方式复制查询到的实体得到新实例,常见实现方式包括手动编写拷贝构造函数、序列化反序列化、使用AutoMapper等映射库配置深度映射。
内容的提问来源于stack exchange,提问作者Jakub
相关产品推荐
相关产品推荐

