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

EF Core 6.0 自引用实体操作触发循环依赖异常问题咨询

问题描述

我认为这可能是EF Core 6.0的Bug,但在向EF Core GitHub仓库提交Bug前,想先在此咨询确认。
我无法完全确定这是Bug,至少它属于不符合预期的行为。
该行为是我迁移到.NET 6后首次出现的,我已经通过下方3个文件组成的简单项目复现了该问题。最初我以为这是一项破坏性变更,所以查阅了所有相关变更文档,但未找到任何会触发该错误的破坏性变更说明。由于无法将错误与已知变更关联,我开始修改.csproj文件中的EF Core版本,当我将所有EF Core相关包版本切换为6.0.0-rc.2.21480.5后,错误就不再出现了。
另外需要注意的是,如果我在Program.cs的删除操作后添加一次SaveChanges调用,错误也不会出现。
请问各位认为这是EF Core 6.0的Bug,还是我遗漏了某项新特性或破坏性变更?

Person.cs

#nullable disable
public class Person
{
    public int PersonId { get; set; }
    public string Name { get; set; }
    public int? ParentId { get; set; }
    public Person Parent { get; set; }

    protected Person()
    {
    }
    
    public Person(string name, Person parent)
    {
        this.Name = name;

        if (parent is not null)
        {
            this.Parent = parent;
        }
    }
}

Context.cs

#nullable disable
using Microsoft.EntityFrameworkCore;

public class Context : DbContext
{
    public Context()
    {}

    public DbSet<Person> Parents { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(
            "Data Source=DES04;Initial Catalog=Test;Integrated Security=true");        
        base.OnConfiguring(optionsBuilder);
    }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Person>()
            .HasOne(p => p.Parent)
            .WithMany()
            .IsRequired(false)
            .OnDelete(DeleteBehavior.Restrict);

        modelBuilder.Entity<Person>()
            .HasKey(p => p.PersonId);
    }
}

Program.cs

var context = new Context();

var parent = new Person("1", null);
var child1 = new Person("2", parent);
var child2 = new Person("3", parent);
var grandchild1 = new Person("4", child1);
var grandchild2 = new Person("5", child1);
var grandchild3 = new Person("6", child2);
var grandchild4 = new Person("7", child2);

context.Add(parent);
context.Add(child1);
context.Add(child2);
context.Add(grandchild1);
context.Add(grandchild2);
context.Add(grandchild3);
context.Add(grandchild4);
context.SaveChanges();

context.Remove(parent);
context.Remove(child1);
context.Remove(child2);
context.Remove(grandchild1);
context.Remove(grandchild2);
context.Remove(grandchild3);
context.Remove(grandchild4);

parent = new Person("1", null);
child1 = new Person("2", parent);
child2 = new Person("3", parent);
grandchild1 = new Person("4", child1);
grandchild2 = new Person("5", child1);
grandchild3 = new Person("6", child2);
grandchild4 = new Person("7", child2);

context.Add(parent);
context.Add(child1);
context.Add(child2);
context.Add(grandchild1);
context.Add(grandchild2);
context.Add(grandchild3);
context.Add(grandchild4);
context.SaveChanges();

堆栈跟踪

未处理的异常。System.InvalidOperationException: 无法保存更改,因为在待保存数据中检测到循环依赖:'Person [Deleted]Person [Deleted] Parent { 'ParentId' } <-
Person [Deleted] Parent { 'ParentId' } <-
Person [Deleted]如需查看更多信息,请调用'DbContextOptionsBuilder.EnableSensitiveDataLogging'。

at Microsoft.EntityFrameworkCore.Utilities.Multigraph2.ThrowCycle(List1 cycle, Func2 formatCycle, Func2 formatException)
at Microsoft.EntityFrameworkCore.Utilities.Multigraph2.BatchingTopologicalSort(Func4 tryBreakEdge, Func2 formatCycle) at Microsoft.EntityFrameworkCore.Update.Internal.CommandBatchPreparer.TopologicalSort(IEnumerable1 commands)
at Microsoft.EntityFrameworkCore.Update.Internal.CommandBatchPreparer.BatchCommands(IList1 entries, IUpdateAdapter updateAdapter)+MoveNext() at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(IEnumerable1 commandBatches, IRelationalConnection connection)
at Microsoft.EntityFrameworkCore.Storage.RelationalDatabase.SaveChanges(IList1 entries) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(IList1 entriesToSave)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(StateManager stateManager, Boolean acceptAllChangesOnSuccess)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.<>c.b__104_0(DbContext _, ValueTuple2 t) at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func3 operation, Func`3 verifySucceeded)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(Boolean acceptAllChangesOnSuccess)
at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)
at Microsoft.EntityFrameworkCore.DbContext.SaveChanges()
at Program.$(String[] args) in C:\Users\Dev04\Desktop\Nueva carpeta\test\Program.cs:line 44

版本信息

  • EF Core: Microsoft.EntityFrameworkCore 6.0.0
  • 数据库提供程序: Microsoft.EntityFrameworkCore.SqlServer 6.0.0
  • 目标框架: .NET 6.0.0

解答

这是EF Core 6.0.0正式版的已知Bug,并非你使用方式有误或者遗漏了破坏性变更。

根因说明

EF Core 6.0.0正式版在优化变更集的拓扑排序逻辑时,对自引用实体的同批次删除+新增场景处理存在缺陷:当你在同一个SaveChanges批次中,同时提交存在关联的已删除自引用实体、以及新的同名关联自引用实体时,排序逻辑会错误识别出不存在的循环依赖,抛出对应的异常。

你观测到的两个现象也完全匹配这个根因:

  • 切换到6.0.0-rc.2版本不会复现,是因为这个有问题的排序优化是在rc2版本之后才合并到正式版的
  • 删除操作后加一次SaveChanges就正常,是因为删除和新增操作分两个批次提交,同批次变更集不存在冲突的实体关联关系

解决办法

  1. 优先方案:升级EF Core版本
    微软已经在EF Core 6.0.1及之后的补丁版本中修复了这个Bug,你只需要将项目中所有EF Core相关包的版本升级到6.0.1或更高,不需要修改现有业务代码就能解决问题。
  2. 临时兼容方案
    如果你暂时无法升级EF Core版本,可以选择以下任意一种方式规避:
  • 删除实体前先手动清空所有实体的Parent导航属性引用,再执行Remove操作
  • 按照你测试的逻辑,删除操作完成后单独调用一次SaveChanges,再执行后续新增逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 14:24:08