未调用ToList时出现命令关联已打开DataReader错误如何解决
问题根源
你当前传入的IEnumerable<Ques>参数是未立即执行的EF查询实例,遍历该序列时底层DataReader会保持打开状态,循环内调用Reference.Load()会尝试复用同一个连接开启新的查询,两个活动DataReader冲突就触发了这个报错。加ToList()本质是先把data的查询结果全部加载到内存、关闭第一层DataReader再遍历,和开MARS都是常规解法但你都不能用,所以最优解法是改为批量预加载关联数据,性能反而比你原来循环单条Load的实现更高。
可行解决方案
方案:批量拉取关联用户后手动赋值
全程只有两次数据库查询,不会有并发DataReader问题,不需要修改配置、不需要对原数据做全量内存拷贝:
public override void Load(IEnumerable<Ques> data) { // 第一步:仅遍历收集所有非空的用户ID,不加载完整Ques实体到内存 var userIds = new List<int>(); using (var enumerator = data.GetEnumerator()) { while (enumerator.MoveNext()) { var item = enumerator.Current; if (item?.Id_user != null) { userIds.Add(item.Id_user.Value); } } } if (!userIds.Any()) return; // 第二步:批量查询所有需要的用户实体,存入字典方便快速匹配 var userDict = db.Users .Where(u => userIds.Contains(u.Id)) .ToDictionary(u => u.Id); // 第三步:再次遍历原序列,手动给导航属性赋值 using (var enumerator = data.GetEnumerator()) { while (enumerator.MoveNext()) { var item = enumerator.Current; if (item?.Id_user != null && userDict.TryGetValue(item.Id_user.Value, out var user)) { item.Users = user; // 标记导航属性已加载,避免EF后续重复触发懒加载,和原生Load行为完全一致 db.Entry(item).Reference(q => q.Users).IsLoaded = true; } } } }
优势说明
- 两次数据库交互,比原实现循环调用N次
Load()的性能提升非常明显,无额外性能损耗 - 不需要对原
data序列执行ToList全量加载,不会额外占用内存存储完整Ques实体集合 - 不需要开启MARS,不需要修改任何配置文件
内容的提问来源于stack exchange,提问作者Coder-Meca
相关产品推荐
相关产品推荐

