Skip to content

Commit

Permalink
Detatching principal keeps track of unreferenced dependents (#25702)
Browse files Browse the repository at this point in the history
  • Loading branch information
ajcvickers authored Aug 27, 2021
1 parent 22644dc commit fdf3998
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 3 deletions.
19 changes: 16 additions & 3 deletions src/EFCore/ChangeTracking/Internal/StateManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -587,10 +587,23 @@ public virtual void StopTracking(InternalEntityEntry entry, EntityState oldState
_internalEntityEntrySubscriber.Unsubscribe(entry);
}

var entityType = entry.EntityType;

foreach (var key in entityType.GetKeys())
foreach (var key in entry.EntityType.GetKeys())
{
foreach (var foreignKey in key.GetReferencingForeignKeys())
{
var dependentToPrincipal = foreignKey.DependentToPrincipal;
if (dependentToPrincipal != null)
{
foreach (InternalEntityEntry dependentEntry in GetDependents(entry, foreignKey))
{
if (dependentEntry[dependentToPrincipal] == entry.Entity)
{
RecordReferencedUntrackedEntity(entry.Entity, dependentToPrincipal, dependentEntry);
}
}
}
}

FindIdentityMap(key)?.Remove(entry);
}

Expand Down
58 changes: 58 additions & 0 deletions test/EFCore.Tests/ChangeTracking/Internal/FixupTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3281,5 +3281,63 @@ protected internal override void OnModelCreating(ModelBuilder modelBuilder)
});
}
}

private class Dependent
{
public int Id { get; set; }
public string Url { get; set; }
public Principal Principal { get; set; }
}

private class Principal
{
public int Id { get; set; }
public string Title { get; set; }
}

private class DetachingContext : DbContext
{
public DbSet<Principal> Principals { get; set; }
public DbSet<Dependent> Dependents { get; set; }

protected internal override void OnConfiguring(DbContextOptionsBuilder options)
=> options
.UseInternalServiceProvider(InMemoryFixture.DefaultServiceProvider)
.UseInMemoryDatabase(nameof(DetachingContext));
}

[ConditionalFact] // Issue #21949
public void Detatching_principal_tracks_unreferenced_foreign_keys()
{
using var context = new DetachingContext();

var dependent = new Dependent { Url = "http://myblog.net" };
var principal = new Principal { Title = "Hello World" };
dependent.Principal = principal;
context.AddRange(dependent, principal);

Assert.Equal(EntityState.Added, context.Entry(principal).State);
Assert.Equal(EntityState.Added, context.Entry(dependent).State);

var principalId = principal.Id;
Assert.Equal(principalId, context.Entry(dependent).Property<int?>("PrincipalId").CurrentValue);
Assert.Same(principal, dependent.Principal);

context.Entry(principal).State = EntityState.Detached;

Assert.Equal(EntityState.Detached, context.Entry(principal).State);
Assert.Equal(EntityState.Added, context.Entry(dependent).State);

principal.Id = 0; // So it re-adds

context.Add(principal);
Assert.NotEqual(principalId, principal.Id);

Assert.Equal(principal.Id, context.Entry(dependent).Property<int?>("PrincipalId").CurrentValue);
Assert.Same(principal, dependent.Principal);

Assert.Equal(EntityState.Added, context.Entry(principal).State);
Assert.Equal(EntityState.Added, context.Entry(dependent).State);
}
}
}

0 comments on commit fdf3998

Please sign in to comment.