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

添加记录时触发Entity Framework Object Disposed Exception问题排查

问题背景

定义了Group和Item实体类,配置了二者的多对多关系(中间表GroupItems)以及Group与主Item的关联。执行CreateGroup方法向Groups表插入记录时,调用AddAsync(group)触发System.ObjectDisposedException异常。应用内同类配置均正常,已开启敏感数据日志但未获取有效信息,怀疑EF特性导致,寻求排查方向及调试技巧。

实体类代码

public class Group
{
    public virtual int Id { get; set; }
    public string? Name { get; set; }            

    [NotMapped]
    public IEnumerable<Item>? Items { get; set; }   
    public Item? Main { get; set; }
}

public class Item
{
    public virtual int Id { get => base.Id ; set => base.Id = value; }

    [NotMapped]
    public IEnumerable<Group>? Groups { get; set; }
}

配置代码

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<Group>()
       .HasMany(g => g.Items)
       .WithMany(u => u.Groups)
       .UsingEntity(j => j.ToTable("GroupItems"));

    modelBuilder.Entity<Group>()
       .HasOne(a => a.Main);               
}

业务代码

public async Task<Group> CreateGroup(string name, int id)
{
    var mainItem = await dbContext.Items
            .SingleAsync(a => a.Id == id);

    var group = new Group()
    {
        Name = name,
        Main = mainItem
    };

    await dbContext.Groups
        .AddAsync(group); // 此处触发异常
    int result = await dbContext
        .SaveChangesAsync();

    return group;
}

异常信息

System.ObjectDisposedException HResult=0x80131622 Message=Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances. ObjectDisposed_ObjectName_Name

日志配置

public static void ConfigureServices(this IServiceCollection services,
                                     string connectionString) =>
    services.AddDbContext<MyDbContext>(a =>
    {
        a.UseSqlServer(connectionString);
        a.EnableSensitiveDataLogging();
    });

排查方向
  • DbContext生命周期冲突
    • 检查MyDbContext的注入方式:默认AddDbContext是Scoped生命周期,若业务代码中存在手动调用Dispose()、用using包裹上下文的情况,会导致后续操作时上下文已被释放。
    • 确认CreateGroup所在类的生命周期:若类是Singleton,而上下文是Scoped,会出现Singleton持有已超出作用域的上下文实例,导致上下文被提前回收。
  • EF多对多配置矛盾
    • 实体类中Items和Groups标记了[NotMapped],但配置代码中又用这两个属性定义多对多关系,导致EF元数据冲突,触发内部状态跟踪异常。
    • 多对多导航属性使用IEnumerable<T>是只读类型,EF需要可修改的ICollection<T>来跟踪关联状态,只读集合会导致跟踪逻辑异常。
  • 主Item跟踪状态异常
    • 检查mainItem是否被上下文正常跟踪:若查询时使用了AsNoTracking()(全局配置或隐式调用),或上下文在查询后被释放,会导致mainItem成为游离对象,关联到Group时EF尝试访问已释放的上下文。
调试技巧
  • 断点跟踪上下文状态
    • 在CreateGroup开头和AddAsync前添加断点,查看dbContext的ContextId和IsDisposed属性,确认上下文实例是否一致、是否已被标记为释放。
    • 通过dbContext.Entry(mainItem).State检查mainItem的跟踪状态,正常应为Unchanged,若为Detached则说明上下文已无法跟踪该对象。
  • 验证配置冲突
    • 临时注释多对多配置代码,仅保留Group与Main的一对一关联,测试AddAsync是否正常,排除多对多配置的影响。
    • 将IEnumerable<Item>和IEnumerable<Group>改为ICollection<Item>和ICollection<Group>,去掉[NotMapped],让EF正确识别导航属性后重新测试。
  • 启用EF详细日志
    • 修改日志配置,输出Debug级别的EF内部操作日志:
      services.AddDbContext<MyDbContext>(a =>
      {
          a.UseSqlServer(connectionString);
          a.EnableSensitiveDataLogging();
          a.LogTo(Console.WriteLine, LogLevel.Debug);
      });
      
      重点关注上下文状态变化、实体跟踪逻辑的日志信息。
  • 验证生命周期问题
    • 在CreateGroup所在类注入IServiceScopeFactory,手动创建作用域获取上下文测试:
      public async Task<Group> CreateGroup(string name, int id)
      {
          using var scope = _serviceScopeFactory.CreateScope();
          var scopedContext = scope.ServiceProvider.GetRequiredService<MyDbContext>();
          var mainItem = await scopedContext.Items.SingleAsync(a => a.Id == id);
          var group = new Group() { Name = name, Main = mainItem };
          await scopedContext.Groups.AddAsync(group);
          await scopedContext.SaveChangesAsync();
          return group;
      }
      
      若操作正常,说明原上下文生命周期存在问题。

修正后的关键代码示例

实体类调整

public class Group
{
    public virtual int Id { get; set; }
    public string? Name { get; set; }            

    public ICollection<Item>? Items { get; set; }   
    public Item? Main { get; set; }
}

public class Item
{
    public virtual int Id { get; set; }

    public ICollection<Group>? Groups { get; set; }
}

配置类调整

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<Group>()
       .HasMany(g => g.Items)
       .WithMany(u => u.Groups)
       .UsingEntity(j => j.ToTable("GroupItems"));

    // 显式配置一对一关联的外键,避免自动生成歧义
    modelBuilder.Entity<Group>()
       .HasOne(a => a.Main)
       .WithOne()
       .HasForeignKey<Group>("MainItemId");
}

内容的提问来源于stack exchange,提问作者sid

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 12:07:45