Skip to content

SaveChanges circular dependency in unique unfiltered index (when change doesn't affect any column from unique index). #30601

@jmalczak

Description

@jmalczak

When using Attach and IsModified to update column without first fetching whole entity from database, SaveChanges throws circular dependency in unique unfiltered index, even if updated column is not part of the index. This code works fine in ef core 6.

Repro steps

 [Fact]
    public void SaveChangesShouldNotThrow()
    {
        var context = new TestDbContext();

        var e = new TestEntity { Id = Guid.NewGuid(), Name = "n1" };
        var e2 = new TestEntity { Id = Guid.NewGuid(), Name = "n2" };

        context.TestEntities.Attach(e);
        context.TestEntities.Attach(e2);

        context.Entry(e).Property(p => p.Name).IsModified = true;
        context.Entry(e2).Property(p => p.Name).IsModified = true;

        context.SaveChanges();
    }

    public class TestDbContext : DbContext
    {
        public DbSet<TestEntity> TestEntities { get; set; }

        protected override void OnConfiguring(
            DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured || optionsBuilder.Options.Extensions.Count() <= 1)
            {
                optionsBuilder.UseSqlServer("Server=.;Initial Catalog=test;User Id=sa;Password=test;TrustServerCertificate=true");

                base.OnConfiguring(optionsBuilder);
            }
        }

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

            modelBuilder.Entity<TestEntity>()
                .HasKey(t => t.Id);

            modelBuilder.Entity<TestEntity>()
                .HasIndex(t => new { t.Id1, t.Id2 })
                .IsUnique();
        }
    }

    public class TestEntity
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public Guid Id1 { get; set; }
        public Guid Id2 { get; set; }
    }

Stack trace

System.InvalidOperationException
Unable to save changes because a circular dependency was detected in the data to be saved: 'TestEntity [Modified] <-
Index { 'Id1', 'Id2' } TestEntity [Modified] <-
Index { 'Id1', 'Id2' } TestEntity [Modified]To show additional information call 'DbContextOptionsBuilder.EnableSensitiveDataLogging'.'.
   at Microsoft.EntityFrameworkCore.Utilities.Multigraph`2.ThrowCycle(List`1 cycle, Func`2 formatCycle, Func`2 formatException)
   at Microsoft.EntityFrameworkCore.Utilities.Multigraph`2.TopologicalSortCore(Boolean withBatching, Func`4 tryBreakEdge, Func`2 formatCycle, Func`2 formatException)
   at Microsoft.EntityFrameworkCore.Utilities.Multigraph`2.BatchingTopologicalSort(Func`4 tryBreakEdge, Func`2 formatCycle, Func`2 formatException)
   at Microsoft.EntityFrameworkCore.Update.Internal.CommandBatchPreparer.TopologicalSort(IEnumerable`1 commands)
   at Microsoft.EntityFrameworkCore.Update.Internal.CommandBatchPreparer.BatchCommands(IList`1 entries, IUpdateAdapter updateAdapter)+MoveNext()
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(IEnumerable`1 commandBatches, IRelationalConnection connection)
   at Microsoft.EntityFrameworkCore.Storage.RelationalDatabase.SaveChanges(IList`1 entries)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(IList`1 entriesToSave)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(StateManager stateManager, Boolean acceptAllChangesOnSuccess)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.<>c.<SaveChanges>b__107_0(DbContext _, ValueTuple`2 t)
   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 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()**
**

Include provider and version information

EF Core version:
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 7.0.202, ef core 7.0.3 (it was working fine in ef core 6.*)

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions