You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DbSet.Local遍历触发‘Collection was modified; enumeration operation may not execute’异常的原因咨询

DbSet.Local遍历触发‘Collection was modified; enumeration operation may not execute’异常的原因咨询

嘿,我来给你捋清楚这个问题的来龙去脉,你遇到的这个异常其实是集合枚举的经典“坑”,完全不是你的代码写错了,而是DbSet.Local的特性导致的~

核心原因:DbSet.Local是动态集合,枚举时不允许修改

_ctx.Set<TObject>().Local本质是一个动态的ObservableCollection,它会实时反映当前DbContext中被跟踪的实体状态。当你直接枚举这个集合(比如你的原始InCache方法直接返回它)的时候,.NET的枚举器会检测集合的版本号——如果在枚举过程中(或者前后两次迭代之间)集合被修改(添加/删除元素),就会抛出Collection was modified异常,这是为了避免枚举结果不一致的问题。

你的代码触发异常的具体流程

我们来拆解你的循环逻辑:

  1. 第一次循环:
    • 调用GetByCache,先查Local集合(此时没有对应Entity),然后从数据库加载该实体,加载后EF会自动把这个实体加入Local集合。
    • 接着调用_repo.Insert(me),如果是新创建的Entity,EF也会把它加入Local集合。
  2. 第二次循环:
    • 再次调用GetByCache,此时你直接枚举Local集合(原始InCache返回的就是它本身),但这个集合在第一次循环中已经被修改过(新增了元素),枚举器检测到集合版本变化,直接抛出异常。

为什么ToList()能解决问题?

你修改后的InCache方法用ToList()把Local集合的元素一次性复制到一个静态的List中,这个新List是独立于DbSet.Local的,后续对Local的修改不会影响它。枚举这个静态集合时,因为它的状态不会再变化,自然不会触发修改检测的异常。

关于你“不舒服”的小补充

其实这个解决方案完全合理,不用纠结~如果担心性能开销,一般来说Local集合里的实体数量不会特别多(毕竟是当前上下文跟踪的实体),ToList()的复制成本可以忽略不计。

另外,也可以给你一个小优化建议:在GetByCache里直接对Local做查询时,先转成List再查,避免每次都单独写ToList():

public T GetByCache<T>(Expression<Func<T, bool>> w) {
    var localEntities = _ctx.Set<T>().Local.ToList(); // 先转成静态List
    T e = localEntities.Where(w.Compile()).FirstOrDefault();  
    if (e == null)
        e = AllIncluding<T>().Where(w).FirstOrDefault();
    return e;
}

备注:内容来源于stack exchange,提问作者tschmit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.21 14:40:32