为什么一行未使用的代码会导致Entity Framework出现实体附加错误?
报错核心原因和Entity Framework的跟踪机制直接相关
- 默认情况下,EF的
DbContext会对所有返回完整实体对象的查询开启自动跟踪:查询得到的实体实例会被缓存在上下文的状态管理器中,以实体类型+主键值作为唯一标识,同一个上下文不允许同时跟踪两个主键相同的同类型实体。
你标注的那行代码:
List<Place> PlacesList = context.Places.Where(x => x.PlaceNumberID == item.PlaceNumberID).ToList();
是直接查询完整的Place实体集合,查询执行后,返回的所有Place实例都会被当前DbContext跟踪,对应的主键(也就是PlaceId)已经被状态管理器记录。
后续你执行Attach(item)的时候,item是来自ListOfPlaces的外部实例,不在当前上下文的跟踪范围内,但你已经给它的主键ID赋值为和之前查询到的Place实例完全相同的PlaceId,这就触发了EF的主键冲突校验,直接抛出你看到的报错。
为什么注释掉那行代码就正常
注释掉完整实体查询后,后续的查询是投影查询:
long PlaceId = context.Places .Where(x => x.PlaceNumberID == item.PlaceNumberID) .Select(x => x.PlaceId) .FirstOrDefault();
这个查询只返回单个long类型的字段值,没有返回完整的Place实体对象,不会触发实体跟踪,上下文的状态管理器里没有对应主键的Place实例,所以Attach外部的item实例不会出现冲突。
额外优化建议
你当前的代码其实对同一个条件做了两次数据库查询,完全可以合并成一次来减少IO开销:要么直接删除多余的整行查询(最优,本来就是无用代码),要么直接复用第一次查询的结果拿PlaceId,不需要再查第二次。
内容的提问来源于stack exchange,提问作者Jay
相关产品推荐
相关产品推荐

