From ca581305f9e9f15f89ccb7ea4f2ba2480d1f0e3b Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Sun, 18 Sep 2022 11:55:47 +0100 Subject: [PATCH] Don't fixup to deleted entities The new nested entity was being fixed up to the deleted double-nested entities. Fixes #29085 --- .../Internal/NavigationFixer.cs | 6 +- .../FieldsOnlyLoadTestBase.cs | 309 +++++++-- .../ProxyGraphUpdatesTestBaseOneToOne.cs | 6 +- .../ProxyGraphUpdatesTestBaseOneToOneAk.cs | 6 +- .../LazyLoadProxyTestBase.cs | 231 ++++++- .../LoadTestBase.cs | 498 +++++++++++--- .../ChangeTracking/Internal/OwnedFixupTest.cs | 632 +++++++++++++----- 7 files changed, 1353 insertions(+), 335 deletions(-) diff --git a/src/EFCore/ChangeTracking/Internal/NavigationFixer.cs b/src/EFCore/ChangeTracking/Internal/NavigationFixer.cs index 34b4dc6285d..a871f65b241 100644 --- a/src/EFCore/ChangeTracking/Internal/NavigationFixer.cs +++ b/src/EFCore/ChangeTracking/Internal/NavigationFixer.cs @@ -780,7 +780,8 @@ private void InitialFixup( if (foreignKey.IsUnique) { var dependentEntry = (InternalEntityEntry?)dependents.FirstOrDefault(); - if (dependentEntry != null) + if (dependentEntry != null + && dependentEntry.EntityState != EntityState.Deleted) { var toDependent = foreignKey.PrincipalToDependent; if (CanOverrideCurrentValue(entry, toDependent, dependentEntry, fromQuery) @@ -796,7 +797,8 @@ private void InitialFixup( { foreach (InternalEntityEntry dependentEntry in dependents) { - if (!IsAmbiguous(dependentEntry) + if (dependentEntry.EntityState != EntityState.Deleted + && !IsAmbiguous(dependentEntry) && (!fromQuery || CanOverrideCurrentValue(dependentEntry, foreignKey.DependentToPrincipal, entry, fromQuery))) { SetNavigation(dependentEntry, foreignKey.DependentToPrincipal, entry, fromQuery); diff --git a/test/EFCore.Specification.Tests/FieldsOnlyLoadTestBase.cs b/test/EFCore.Specification.Tests/FieldsOnlyLoadTestBase.cs index cf074082023..de7fbf3fac1 100644 --- a/test/EFCore.Specification.Tests/FieldsOnlyLoadTestBase.cs +++ b/test/EFCore.Specification.Tests/FieldsOnlyLoadTestBase.cs @@ -232,8 +232,16 @@ public virtual async Task Load_many_to_one_reference_to_principal(EntityState st var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.Children.Single()); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.Children); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.Children.Single()); + } } [ConditionalTheory] @@ -273,8 +281,16 @@ public virtual async Task Load_one_to_one_reference_to_principal(EntityState sta var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.Single); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.Single); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.Single); + } } [ConditionalTheory] @@ -316,8 +332,16 @@ public virtual async Task Load_one_to_one_reference_to_principal_when_NoTracking var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.Single); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.Single); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.Single); + } } [ConditionalTheory] @@ -398,8 +422,16 @@ public virtual async Task Load_one_to_one_PK_to_PK_reference_to_principal(Entity var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SinglePkToPk); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SinglePkToPk); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SinglePkToPk); + } } [ConditionalTheory] @@ -503,13 +535,22 @@ public virtual async Task Load_many_to_one_reference_to_principal_using_Query(En ? await referenceEntry.Query().SingleAsync() : referenceEntry.Query().Single(); - Assert.True(referenceEntry.IsLoaded); + Assert.Equal(state != EntityState.Deleted, referenceEntry.IsLoaded); RecordLog(); Assert.NotNull(parent); - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.Children.Single()); + + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.Children); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.Children.Single()); + } Assert.Equal(2, context.ChangeTracker.Entries().Count()); } @@ -538,13 +579,22 @@ public virtual async Task Load_one_to_one_reference_to_principal_using_Query(Ent ? await referenceEntry.Query().SingleAsync() : referenceEntry.Query().Single(); - Assert.True(referenceEntry.IsLoaded); + Assert.Equal(state != EntityState.Deleted, referenceEntry.IsLoaded); RecordLog(); Assert.NotNull(parent); - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.Single); + + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.Single); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.Single); + } Assert.Equal(2, context.ChangeTracker.Entries().Count()); } @@ -608,13 +658,22 @@ public virtual async Task Load_one_to_one_PK_to_PK_reference_to_principal_using_ ? await referenceEntry.Query().SingleAsync() : referenceEntry.Query().Single(); - Assert.True(referenceEntry.IsLoaded); + Assert.Equal(state != EntityState.Deleted, referenceEntry.IsLoaded); RecordLog(); Assert.NotNull(parent); - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SinglePkToPk); + + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SinglePkToPk); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SinglePkToPk); + } Assert.Equal(2, context.ChangeTracker.Entries().Count()); } @@ -1696,8 +1755,16 @@ public virtual async Task Load_many_to_one_reference_to_principal_untyped(Entity var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.Children.Single()); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.Children); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.Children.Single()); + } } [ConditionalTheory] @@ -1737,8 +1804,16 @@ public virtual async Task Load_one_to_one_reference_to_principal_untyped(EntityS var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.Single); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.Single); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.Single); + } } [ConditionalTheory] @@ -1844,13 +1919,22 @@ public virtual async Task Load_many_to_one_reference_to_principal_using_Query_un ? (await navigationEntry.Query().ToListAsync()).Single() : navigationEntry.Query().ToList().Single(); - Assert.True(navigationEntry.IsLoaded); + Assert.Equal(state != EntityState.Deleted, navigationEntry.IsLoaded); RecordLog(); Assert.NotNull(parent); - Assert.Same(parent, child.Parent); - Assert.Same(child, ((Parent)parent).Children.Single()); + + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(((Parent)parent).Children); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, ((Parent)parent).Children.Single()); + } Assert.Equal(2, context.ChangeTracker.Entries().Count()); } @@ -1880,13 +1964,22 @@ public virtual async Task Load_one_to_one_reference_to_principal_using_Query_unt ? (await navigationEntry.Query().ToListAsync()).Single() : navigationEntry.Query().ToList().Single(); - Assert.True(navigationEntry.IsLoaded); + Assert.Equal(state != EntityState.Deleted, navigationEntry.IsLoaded); RecordLog(); Assert.NotNull(parent); - Assert.Same(parent, single.Parent); - Assert.Same(single, ((Parent)parent).Single); + + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(((Parent)parent).Single); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, ((Parent)parent).Single); + } Assert.Equal(2, context.ChangeTracker.Entries().Count()); } @@ -2669,8 +2762,16 @@ public virtual async Task Load_many_to_one_reference_to_principal_alternate_key( var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.ChildrenAk.Single()); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.ChildrenAk); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.ChildrenAk.Single()); + } } [ConditionalTheory] @@ -2710,8 +2811,16 @@ public virtual async Task Load_one_to_one_reference_to_principal_alternate_key(E var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SingleAk); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SingleAk); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SingleAk); + } } [ConditionalTheory] @@ -2815,13 +2924,22 @@ public virtual async Task Load_many_to_one_reference_to_principal_using_Query_al ? await referenceEntry.Query().SingleAsync() : referenceEntry.Query().Single(); - Assert.True(referenceEntry.IsLoaded); + Assert.Equal(state != EntityState.Deleted, referenceEntry.IsLoaded); RecordLog(); Assert.NotNull(parent); - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.ChildrenAk.Single()); + + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.ChildrenAk); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.ChildrenAk.Single()); + } Assert.Equal(2, context.ChangeTracker.Entries().Count()); } @@ -2850,13 +2968,22 @@ public virtual async Task Load_one_to_one_reference_to_principal_using_Query_alt ? await referenceEntry.Query().SingleAsync() : referenceEntry.Query().Single(); - Assert.True(referenceEntry.IsLoaded); + Assert.Equal(state != EntityState.Deleted, referenceEntry.IsLoaded); RecordLog(); Assert.NotNull(parent); - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SingleAk); + + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SingleAk); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SingleAk); + } Assert.Equal(2, context.ChangeTracker.Entries().Count()); } @@ -3119,8 +3246,16 @@ public virtual async Task Load_many_to_one_reference_to_principal_shadow_fk(Enti var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.ChildrenShadowFk.Single()); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.ChildrenShadowFk); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.ChildrenShadowFk.Single()); + } } [ConditionalTheory] @@ -3160,8 +3295,16 @@ public virtual async Task Load_one_to_one_reference_to_principal_shadow_fk(Entit var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SingleShadowFk); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SingleShadowFk); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SingleShadowFk); + } } [ConditionalTheory] @@ -3265,13 +3408,22 @@ public virtual async Task Load_many_to_one_reference_to_principal_using_Query_sh ? await referenceEntry.Query().SingleAsync() : referenceEntry.Query().Single(); - Assert.True(referenceEntry.IsLoaded); + Assert.Equal(state != EntityState.Deleted, referenceEntry.IsLoaded); RecordLog(); Assert.NotNull(parent); - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.ChildrenShadowFk.Single()); + + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.ChildrenShadowFk); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.ChildrenShadowFk.Single()); + } Assert.Equal(2, context.ChangeTracker.Entries().Count()); } @@ -3300,13 +3452,22 @@ public virtual async Task Load_one_to_one_reference_to_principal_using_Query_sha ? await referenceEntry.Query().SingleAsync() : referenceEntry.Query().Single(); - Assert.True(referenceEntry.IsLoaded); + Assert.Equal(state != EntityState.Deleted, referenceEntry.IsLoaded); RecordLog(); Assert.NotNull(parent); - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SingleShadowFk); + + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SingleShadowFk); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SingleShadowFk); + } Assert.Equal(2, context.ChangeTracker.Entries().Count()); } @@ -3569,8 +3730,16 @@ public virtual async Task Load_many_to_one_reference_to_principal_composite_key( var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.ChildrenCompositeKey.Single()); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.ChildrenCompositeKey); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.ChildrenCompositeKey.Single()); + } } [ConditionalTheory] @@ -3610,8 +3779,16 @@ public virtual async Task Load_one_to_one_reference_to_principal_composite_key(E var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SingleCompositeKey); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SingleCompositeKey); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SingleCompositeKey); + } } [ConditionalTheory] @@ -3715,13 +3892,22 @@ public virtual async Task Load_many_to_one_reference_to_principal_using_Query_co ? await referenceEntry.Query().SingleAsync() : referenceEntry.Query().Single(); - Assert.True(referenceEntry.IsLoaded); + Assert.Equal(state != EntityState.Deleted, referenceEntry.IsLoaded); RecordLog(); Assert.NotNull(parent); - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.ChildrenCompositeKey.Single()); + + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.ChildrenCompositeKey); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.ChildrenCompositeKey.Single()); + } Assert.Equal(2, context.ChangeTracker.Entries().Count()); } @@ -3750,13 +3936,22 @@ public virtual async Task Load_one_to_one_reference_to_principal_using_Query_com ? await referenceEntry.Query().SingleAsync() : referenceEntry.Query().Single(); - Assert.True(referenceEntry.IsLoaded); + Assert.Equal(state != EntityState.Deleted, referenceEntry.IsLoaded); RecordLog(); Assert.NotNull(parent); - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SingleCompositeKey); + + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SingleCompositeKey); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SingleCompositeKey); + } Assert.Equal(2, context.ChangeTracker.Entries().Count()); } diff --git a/test/EFCore.Specification.Tests/GraphUpdates/ProxyGraphUpdatesTestBaseOneToOne.cs b/test/EFCore.Specification.Tests/GraphUpdates/ProxyGraphUpdatesTestBaseOneToOne.cs index 0f6ac60a8de..25d7672c1b3 100644 --- a/test/EFCore.Specification.Tests/GraphUpdates/ProxyGraphUpdatesTestBaseOneToOne.cs +++ b/test/EFCore.Specification.Tests/GraphUpdates/ProxyGraphUpdatesTestBaseOneToOne.cs @@ -419,9 +419,9 @@ public virtual void Save_required_non_PK_one_to_one_changed_by_reference(ChangeM } else { - new1d.Root = old1d.Root; - new1dd.Root = old1dd.Root; - new1dd.DerivedRoot = old1dd.DerivedRoot; + new1d.RootId = old1d.RootId; + new1dd.RootId = old1dd.RootId; + new1dd.DerivedRootId = old1dd.DerivedRootId; context.AddRange(new1, new1d, new1dd, new2, new2d, new2dd); } diff --git a/test/EFCore.Specification.Tests/GraphUpdates/ProxyGraphUpdatesTestBaseOneToOneAk.cs b/test/EFCore.Specification.Tests/GraphUpdates/ProxyGraphUpdatesTestBaseOneToOneAk.cs index 575e56360f0..9012d02d74b 100644 --- a/test/EFCore.Specification.Tests/GraphUpdates/ProxyGraphUpdatesTestBaseOneToOneAk.cs +++ b/test/EFCore.Specification.Tests/GraphUpdates/ProxyGraphUpdatesTestBaseOneToOneAk.cs @@ -645,9 +645,9 @@ public virtual void Save_required_non_PK_one_to_one_changed_by_reference_with_al } else { - new1d.Root = old1d.Root; - new1dd.Root = old1dd.Root; - new1dd.DerivedRoot = old1dd.DerivedRoot; + new1d.RootId = old1d.RootId; + new1dd.RootId = old1dd.RootId; + new1dd.DerivedRootId = old1dd.DerivedRootId; context.AddRange(new1, new1d, new1dd, new2, new2d, new2dd); } diff --git a/test/EFCore.Specification.Tests/LazyLoadProxyTestBase.cs b/test/EFCore.Specification.Tests/LazyLoadProxyTestBase.cs index 62ec396016e..b24ac9e6d42 100644 --- a/test/EFCore.Specification.Tests/LazyLoadProxyTestBase.cs +++ b/test/EFCore.Specification.Tests/LazyLoadProxyTestBase.cs @@ -394,7 +394,14 @@ public virtual void Lazy_load_many_to_one_reference_to_principal(EntityState sta changeDetector.DetectChangesCalled = false; - Assert.NotNull(child.Parent); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + } + else + { + Assert.NotNull(child.Parent); + } Assert.False(changeDetector.DetectChangesCalled); @@ -407,8 +414,16 @@ public virtual void Lazy_load_many_to_one_reference_to_principal(EntityState sta var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.Children.Single()); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.Children); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.Children.Single()); + } } } @@ -471,7 +486,14 @@ public virtual void Lazy_load_one_to_one_reference_to_principal(EntityState stat changeDetector.DetectChangesCalled = false; - Assert.NotNull(single.Parent); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + } + else + { + Assert.NotNull(single.Parent); + } Assert.True(referenceEntry.IsLoaded); @@ -484,8 +506,16 @@ public virtual void Lazy_load_one_to_one_reference_to_principal(EntityState stat var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.Single); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.Single); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.Single); + } } } @@ -587,7 +617,14 @@ public virtual void Lazy_load_one_to_one_PK_to_PK_reference_to_principal(EntityS changeDetector.DetectChangesCalled = false; - Assert.NotNull(single.Parent); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + } + else + { + Assert.NotNull(single.Parent); + } Assert.False(changeDetector.DetectChangesCalled); @@ -600,8 +637,16 @@ public virtual void Lazy_load_one_to_one_PK_to_PK_reference_to_principal(EntityS var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SinglePkToPk); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SinglePkToPk); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SinglePkToPk); + } } [ConditionalTheory] @@ -860,7 +905,14 @@ public virtual void Lazy_load_many_to_one_reference_to_principal_changed_non_fou context.ChangeTracker.DetectChanges(); - Assert.NotNull(child.Parent); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + } + else + { + Assert.NotNull(child.Parent); + } Assert.True(changeDetector.DetectChangesCalled); @@ -873,8 +925,16 @@ public virtual void Lazy_load_many_to_one_reference_to_principal_changed_non_fou var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(child, parent.Children.Single()); - Assert.Same(parent, child.Parent); + if (state == EntityState.Deleted) + { + Assert.Null(parent.Children); + Assert.Null(child.Parent); + } + else + { + Assert.Same(child, parent.Children.Single()); + Assert.Same(parent, child.Parent); + } } [ConditionalTheory] @@ -914,7 +974,14 @@ public virtual void Lazy_load_many_to_one_reference_to_principal_changed_found_F context.ChangeTracker.DetectChanges(); - Assert.NotNull(child.Parent); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + } + else + { + Assert.NotNull(child.Parent); + } Assert.True(changeDetector.DetectChangesCalled); @@ -927,8 +994,16 @@ public virtual void Lazy_load_many_to_one_reference_to_principal_changed_found_F var newParent = context.ChangeTracker.Entries().Single(e => e.Entity.Id != parent.Id).Entity; - Assert.Same(child, newParent.Children.Single()); - Assert.Same(newParent, child.Parent); + if (state == EntityState.Deleted) + { + Assert.Empty(parent.Children); + Assert.Null(child.Parent); + } + else + { + Assert.Same(child, newParent.Children.Single()); + Assert.Same(newParent, child.Parent); + } } [ConditionalTheory] @@ -1375,7 +1450,14 @@ public virtual void Lazy_load_many_to_one_reference_to_principal_alternate_key(E Assert.False(referenceEntry.IsLoaded); - Assert.NotNull(child.Parent); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + } + else + { + Assert.NotNull(child.Parent); + } Assert.True(referenceEntry.IsLoaded); @@ -1386,8 +1468,16 @@ public virtual void Lazy_load_many_to_one_reference_to_principal_alternate_key(E var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.ChildrenAk.Single()); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.ChildrenAk); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.ChildrenAk.Single()); + } } [ConditionalTheory] @@ -1407,7 +1497,14 @@ public virtual void Lazy_load_one_to_one_reference_to_principal_alternate_key(En Assert.False(referenceEntry.IsLoaded); - Assert.NotNull(single.Parent); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + } + else + { + Assert.NotNull(single.Parent); + } Assert.True(referenceEntry.IsLoaded); @@ -1418,8 +1515,16 @@ public virtual void Lazy_load_one_to_one_reference_to_principal_alternate_key(En var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SingleAk); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SingleAk); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SingleAk); + } } [ConditionalTheory] @@ -1564,7 +1669,14 @@ public virtual void Lazy_load_many_to_one_reference_to_principal_shadow_fk(Entit Assert.False(referenceEntry.IsLoaded); - Assert.NotNull(child.Parent); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + } + else + { + Assert.NotNull(child.Parent); + } Assert.True(referenceEntry.IsLoaded); @@ -1575,8 +1687,16 @@ public virtual void Lazy_load_many_to_one_reference_to_principal_shadow_fk(Entit var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.ChildrenShadowFk.Single()); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.ChildrenShadowFk); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.ChildrenShadowFk.Single()); + } } [ConditionalTheory] @@ -1596,7 +1716,14 @@ public virtual void Lazy_load_one_to_one_reference_to_principal_shadow_fk(Entity Assert.False(referenceEntry.IsLoaded); - Assert.NotNull(single.Parent); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + } + else + { + Assert.NotNull(single.Parent); + } Assert.True(referenceEntry.IsLoaded); @@ -1607,8 +1734,16 @@ public virtual void Lazy_load_one_to_one_reference_to_principal_shadow_fk(Entity var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SingleShadowFk); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SingleShadowFk); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SingleShadowFk); + } } [ConditionalTheory] @@ -1753,7 +1888,14 @@ public virtual void Lazy_load_many_to_one_reference_to_principal_composite_key(E Assert.False(referenceEntry.IsLoaded); - Assert.NotNull(child.Parent); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + } + else + { + Assert.NotNull(child.Parent); + } Assert.True(referenceEntry.IsLoaded); @@ -1764,8 +1906,16 @@ public virtual void Lazy_load_many_to_one_reference_to_principal_composite_key(E var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.ChildrenCompositeKey.Single()); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.ChildrenCompositeKey); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.ChildrenCompositeKey.Single()); + } } [ConditionalTheory] @@ -1785,7 +1935,14 @@ public virtual void Lazy_load_one_to_one_reference_to_principal_composite_key(En Assert.False(referenceEntry.IsLoaded); - Assert.NotNull(single.Parent); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + } + else + { + Assert.NotNull(single.Parent); + } Assert.True(referenceEntry.IsLoaded); @@ -1796,8 +1953,16 @@ public virtual void Lazy_load_one_to_one_reference_to_principal_composite_key(En var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SingleCompositeKey); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SingleCompositeKey); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SingleCompositeKey); + } } [ConditionalTheory] diff --git a/test/EFCore.Specification.Tests/LoadTestBase.cs b/test/EFCore.Specification.Tests/LoadTestBase.cs index 02df72f9019..b4bd6b9df51 100644 --- a/test/EFCore.Specification.Tests/LoadTestBase.cs +++ b/test/EFCore.Specification.Tests/LoadTestBase.cs @@ -212,7 +212,14 @@ public virtual void Lazy_load_many_to_one_reference_to_principal(EntityState sta changeDetector.DetectChangesCalled = false; - Assert.NotNull(child.Parent); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + } + else + { + Assert.NotNull(child.Parent); + } Assert.False(changeDetector.DetectChangesCalled); @@ -225,8 +232,16 @@ public virtual void Lazy_load_many_to_one_reference_to_principal(EntityState sta var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.Children.Single()); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.Children); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.Children.Single()); + } } [ConditionalTheory] @@ -250,7 +265,14 @@ public virtual void Lazy_load_one_to_one_reference_to_principal(EntityState stat changeDetector.DetectChangesCalled = false; - Assert.NotNull(single.Parent); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + } + else + { + Assert.NotNull(single.Parent); + } Assert.False(changeDetector.DetectChangesCalled); @@ -263,8 +285,16 @@ public virtual void Lazy_load_one_to_one_reference_to_principal(EntityState stat var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.Single); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.Single); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.Single); + } } [ConditionalTheory] @@ -326,7 +356,14 @@ public virtual void Lazy_load_one_to_one_PK_to_PK_reference_to_principal(EntityS changeDetector.DetectChangesCalled = false; - Assert.NotNull(single.Parent); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + } + else + { + Assert.NotNull(single.Parent); + } Assert.False(changeDetector.DetectChangesCalled); @@ -339,8 +376,16 @@ public virtual void Lazy_load_one_to_one_PK_to_PK_reference_to_principal(EntityS var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SinglePkToPk); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SinglePkToPk); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SinglePkToPk); + } } [ConditionalTheory] @@ -865,7 +910,14 @@ public virtual void Lazy_load_many_to_one_reference_to_principal_alternate_key(E Assert.False(referenceEntry.IsLoaded); - Assert.NotNull(child.Parent); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + } + else + { + Assert.NotNull(child.Parent); + } Assert.True(referenceEntry.IsLoaded); @@ -876,8 +928,16 @@ public virtual void Lazy_load_many_to_one_reference_to_principal_alternate_key(E var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.ChildrenAk.Single()); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.ChildrenAk); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.ChildrenAk.Single()); + } } [ConditionalTheory] @@ -897,7 +957,14 @@ public virtual void Lazy_load_one_to_one_reference_to_principal_alternate_key(En Assert.False(referenceEntry.IsLoaded); - Assert.NotNull(single.Parent); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + } + else + { + Assert.NotNull(single.Parent); + } Assert.True(referenceEntry.IsLoaded); @@ -908,8 +975,16 @@ public virtual void Lazy_load_one_to_one_reference_to_principal_alternate_key(En var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SingleAk); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SingleAk); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SingleAk); + } } [ConditionalTheory] @@ -1050,7 +1125,14 @@ public virtual void Lazy_load_many_to_one_reference_to_principal_shadow_fk(Entit Assert.False(referenceEntry.IsLoaded); - Assert.NotNull(child.Parent); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + } + else + { + Assert.NotNull(child.Parent); + } Assert.True(referenceEntry.IsLoaded); @@ -1061,8 +1143,16 @@ public virtual void Lazy_load_many_to_one_reference_to_principal_shadow_fk(Entit var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.ChildrenShadowFk.Single()); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.ChildrenShadowFk); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.ChildrenShadowFk.Single()); + } } [ConditionalTheory] @@ -1082,7 +1172,14 @@ public virtual void Lazy_load_one_to_one_reference_to_principal_shadow_fk(Entity Assert.False(referenceEntry.IsLoaded); - Assert.NotNull(single.Parent); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + } + else + { + Assert.NotNull(single.Parent); + } Assert.True(referenceEntry.IsLoaded); @@ -1093,8 +1190,16 @@ public virtual void Lazy_load_one_to_one_reference_to_principal_shadow_fk(Entity var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SingleShadowFk); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SingleShadowFk); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SingleShadowFk); + } } [ConditionalTheory] @@ -1235,7 +1340,14 @@ public virtual void Lazy_load_many_to_one_reference_to_principal_composite_key(E Assert.False(referenceEntry.IsLoaded); - Assert.NotNull(child.Parent); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + } + else + { + Assert.NotNull(child.Parent); + } Assert.True(referenceEntry.IsLoaded); @@ -1246,8 +1358,16 @@ public virtual void Lazy_load_many_to_one_reference_to_principal_composite_key(E var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.ChildrenCompositeKey.Single()); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.ChildrenCompositeKey); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.ChildrenCompositeKey.Single()); + } } [ConditionalTheory] @@ -1267,7 +1387,14 @@ public virtual void Lazy_load_one_to_one_reference_to_principal_composite_key(En Assert.False(referenceEntry.IsLoaded); - Assert.NotNull(single.Parent); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + } + else + { + Assert.NotNull(single.Parent); + } Assert.True(referenceEntry.IsLoaded); @@ -1278,8 +1405,16 @@ public virtual void Lazy_load_one_to_one_reference_to_principal_composite_key(En var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SingleCompositeKey); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SingleCompositeKey); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SingleCompositeKey); + } } [ConditionalTheory] @@ -1545,8 +1680,16 @@ public virtual async Task Load_many_to_one_reference_to_principal(EntityState st var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.Children.Single()); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.Children); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.Children.Single()); + } } [ConditionalTheory] @@ -1586,8 +1729,16 @@ public virtual async Task Load_one_to_one_reference_to_principal(EntityState sta var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.Single); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.Single); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.Single); + } } [ConditionalTheory] @@ -1629,8 +1780,16 @@ public virtual async Task Load_one_to_one_reference_to_principal_when_NoTracking var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.Single); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.Single); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.Single); + } } [ConditionalTheory] @@ -1711,8 +1870,16 @@ public virtual async Task Load_one_to_one_PK_to_PK_reference_to_principal(Entity var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SinglePkToPk); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SinglePkToPk); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SinglePkToPk); + } } [ConditionalTheory] @@ -1816,13 +1983,22 @@ public virtual async Task Load_many_to_one_reference_to_principal_using_Query(En ? await referenceEntry.Query().SingleAsync() : referenceEntry.Query().Single(); - Assert.True(referenceEntry.IsLoaded); + Assert.Equal(state != EntityState.Deleted, referenceEntry.IsLoaded); RecordLog(); Assert.NotNull(parent); - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.Children.Single()); + + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.Children); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.Children.Single()); + } Assert.Equal(2, context.ChangeTracker.Entries().Count()); } @@ -1851,13 +2027,22 @@ public virtual async Task Load_one_to_one_reference_to_principal_using_Query(Ent ? await referenceEntry.Query().SingleAsync() : referenceEntry.Query().Single(); - Assert.True(referenceEntry.IsLoaded); + Assert.Equal(state != EntityState.Deleted, referenceEntry.IsLoaded); RecordLog(); Assert.NotNull(parent); - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.Single); + + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.Single); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.Single); + } Assert.Equal(2, context.ChangeTracker.Entries().Count()); } @@ -1921,13 +2106,22 @@ public virtual async Task Load_one_to_one_PK_to_PK_reference_to_principal_using_ ? await referenceEntry.Query().SingleAsync() : referenceEntry.Query().Single(); - Assert.True(referenceEntry.IsLoaded); + Assert.Equal(state != EntityState.Deleted, referenceEntry.IsLoaded); RecordLog(); Assert.NotNull(parent); - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SinglePkToPk); + + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SinglePkToPk); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SinglePkToPk); + } Assert.Equal(2, context.ChangeTracker.Entries().Count()); } @@ -3009,8 +3203,16 @@ public virtual async Task Load_many_to_one_reference_to_principal_untyped(Entity var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.Children.Single()); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.Children); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.Children.Single()); + } } [ConditionalTheory] @@ -3050,8 +3252,16 @@ public virtual async Task Load_one_to_one_reference_to_principal_untyped(EntityS var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.Single); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.Single); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.Single); + } } [ConditionalTheory] @@ -3157,13 +3367,22 @@ public virtual async Task Load_many_to_one_reference_to_principal_using_Query_un ? (await navigationEntry.Query().ToListAsync()).Single() : navigationEntry.Query().ToList().Single(); - Assert.True(navigationEntry.IsLoaded); + Assert.Equal(state != EntityState.Deleted, navigationEntry.IsLoaded); RecordLog(); Assert.NotNull(parent); - Assert.Same(parent, child.Parent); - Assert.Same(child, ((Parent)parent).Children.Single()); + + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(((Parent)parent).Children); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, ((Parent)parent).Children.Single()); + } Assert.Equal(2, context.ChangeTracker.Entries().Count()); } @@ -3193,13 +3412,22 @@ public virtual async Task Load_one_to_one_reference_to_principal_using_Query_unt ? (await navigationEntry.Query().ToListAsync()).Single() : navigationEntry.Query().ToList().Single(); - Assert.True(navigationEntry.IsLoaded); + Assert.Equal(state != EntityState.Deleted, navigationEntry.IsLoaded); RecordLog(); Assert.NotNull(parent); - Assert.Same(parent, single.Parent); - Assert.Same(single, ((Parent)parent).Single); + + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(((Parent)parent).Single); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, ((Parent)parent).Single); + } Assert.Equal(2, context.ChangeTracker.Entries().Count()); } @@ -3982,8 +4210,16 @@ public virtual async Task Load_many_to_one_reference_to_principal_alternate_key( var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.ChildrenAk.Single()); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.ChildrenAk); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.ChildrenAk.Single()); + } } [ConditionalTheory] @@ -4023,8 +4259,16 @@ public virtual async Task Load_one_to_one_reference_to_principal_alternate_key(E var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SingleAk); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SingleAk); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SingleAk); + } } [ConditionalTheory] @@ -4128,13 +4372,22 @@ public virtual async Task Load_many_to_one_reference_to_principal_using_Query_al ? await referenceEntry.Query().SingleAsync() : referenceEntry.Query().Single(); - Assert.True(referenceEntry.IsLoaded); + Assert.Equal(state != EntityState.Deleted, referenceEntry.IsLoaded); RecordLog(); Assert.NotNull(parent); - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.ChildrenAk.Single()); + + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.ChildrenAk); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.ChildrenAk.Single()); + } Assert.Equal(2, context.ChangeTracker.Entries().Count()); } @@ -4163,13 +4416,22 @@ public virtual async Task Load_one_to_one_reference_to_principal_using_Query_alt ? await referenceEntry.Query().SingleAsync() : referenceEntry.Query().Single(); - Assert.True(referenceEntry.IsLoaded); + Assert.Equal(state != EntityState.Deleted, referenceEntry.IsLoaded); RecordLog(); Assert.NotNull(parent); - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SingleAk); + + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SingleAk); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SingleAk); + } Assert.Equal(2, context.ChangeTracker.Entries().Count()); } @@ -4432,8 +4694,16 @@ public virtual async Task Load_many_to_one_reference_to_principal_shadow_fk(Enti var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.ChildrenShadowFk.Single()); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.ChildrenShadowFk); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.ChildrenShadowFk.Single()); + } } [ConditionalTheory] @@ -4473,8 +4743,16 @@ public virtual async Task Load_one_to_one_reference_to_principal_shadow_fk(Entit var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SingleShadowFk); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SingleShadowFk); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SingleShadowFk); + } } [ConditionalTheory] @@ -4578,13 +4856,22 @@ public virtual async Task Load_many_to_one_reference_to_principal_using_Query_sh ? await referenceEntry.Query().SingleAsync() : referenceEntry.Query().Single(); - Assert.True(referenceEntry.IsLoaded); + Assert.Equal(state != EntityState.Deleted, referenceEntry.IsLoaded); RecordLog(); Assert.NotNull(parent); - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.ChildrenShadowFk.Single()); + + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.ChildrenShadowFk); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.ChildrenShadowFk.Single()); + } Assert.Equal(2, context.ChangeTracker.Entries().Count()); } @@ -4613,13 +4900,22 @@ public virtual async Task Load_one_to_one_reference_to_principal_using_Query_sha ? await referenceEntry.Query().SingleAsync() : referenceEntry.Query().Single(); - Assert.True(referenceEntry.IsLoaded); + Assert.Equal(state != EntityState.Deleted, referenceEntry.IsLoaded); RecordLog(); Assert.NotNull(parent); - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SingleShadowFk); + + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SingleShadowFk); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SingleShadowFk); + } Assert.Equal(2, context.ChangeTracker.Entries().Count()); } @@ -4882,8 +5178,16 @@ public virtual async Task Load_many_to_one_reference_to_principal_composite_key( var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.ChildrenCompositeKey.Single()); + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.ChildrenCompositeKey); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.ChildrenCompositeKey.Single()); + } } [ConditionalTheory] @@ -4923,8 +5227,16 @@ public virtual async Task Load_one_to_one_reference_to_principal_composite_key(E var parent = context.ChangeTracker.Entries().Single().Entity; - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SingleCompositeKey); + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SingleCompositeKey); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SingleCompositeKey); + } } [ConditionalTheory] @@ -5028,13 +5340,22 @@ public virtual async Task Load_many_to_one_reference_to_principal_using_Query_co ? await referenceEntry.Query().SingleAsync() : referenceEntry.Query().Single(); - Assert.True(referenceEntry.IsLoaded); + Assert.Equal(state != EntityState.Deleted, referenceEntry.IsLoaded); RecordLog(); Assert.NotNull(parent); - Assert.Same(parent, child.Parent); - Assert.Same(child, parent.ChildrenCompositeKey.Single()); + + if (state == EntityState.Deleted) + { + Assert.Null(child.Parent); + Assert.Null(parent.ChildrenCompositeKey); + } + else + { + Assert.Same(parent, child.Parent); + Assert.Same(child, parent.ChildrenCompositeKey.Single()); + } Assert.Equal(2, context.ChangeTracker.Entries().Count()); } @@ -5063,13 +5384,22 @@ public virtual async Task Load_one_to_one_reference_to_principal_using_Query_com ? await referenceEntry.Query().SingleAsync() : referenceEntry.Query().Single(); - Assert.True(referenceEntry.IsLoaded); + Assert.Equal(state != EntityState.Deleted, referenceEntry.IsLoaded); RecordLog(); Assert.NotNull(parent); - Assert.Same(parent, single.Parent); - Assert.Same(single, parent.SingleCompositeKey); + + if (state == EntityState.Deleted) + { + Assert.Null(single.Parent); + Assert.Null(parent.SingleCompositeKey); + } + else + { + Assert.Same(parent, single.Parent); + Assert.Same(single, parent.SingleCompositeKey); + } Assert.Equal(2, context.ChangeTracker.Entries().Count()); } diff --git a/test/EFCore.Tests/ChangeTracking/Internal/OwnedFixupTest.cs b/test/EFCore.Tests/ChangeTracking/Internal/OwnedFixupTest.cs index 950a8ac88a2..1efb65a44fe 100644 --- a/test/EFCore.Tests/ChangeTracking/Internal/OwnedFixupTest.cs +++ b/test/EFCore.Tests/ChangeTracking/Internal/OwnedFixupTest.cs @@ -263,8 +263,11 @@ public void Add_principal_with_dependent_both_navs(EntityState entityState, bool var dependent = new Child { Name = "1", Parent = principal }; principal.Child1 = dependent; - var subDependent = new SubChild { Name = "1S", Parent = dependent }; - dependent.SubChild = subDependent; + var subDependent1 = new SubChild { Name = "1S1", Parent = dependent }; + dependent.SubChild1 = subDependent1; + + var subDependent2 = new SubChild { Name = "1S2", Parent = dependent }; + dependent.SubChild2 = subDependent2; if (useTrackGraph == null) { @@ -295,7 +298,7 @@ public void Add_principal_with_dependent_both_navs(EntityState entityState, bool || useTrackGraph == null, context.ChangeTracker.HasChanges()); - Assert.Equal(3, context.ChangeTracker.Entries().Count()); + Assert.Equal(4, context.ChangeTracker.Entries().Count()); AssertFixup( context, @@ -307,11 +310,11 @@ public void Add_principal_with_dependent_both_navs(EntityState entityState, bool Assert.Equal(entityState, context.Entry(principal).State); Assert.Equal(useTrackGraph == null ? EntityState.Added : entityState, context.Entry(dependent).State); - Assert.Same(subDependent, dependent.SubChild); - Assert.Same(dependent, subDependent.Parent); - var subDependentEntry = context.Entry(subDependent); - Assert.Equal(principal.Id, subDependentEntry.Property("ParentId").CurrentValue); - Assert.Equal(useTrackGraph == null ? EntityState.Added : entityState, subDependentEntry.State); + Assert.Same(subDependent1, dependent.SubChild1); + Assert.Same(dependent, subDependent1.Parent); + var subDependentEntry1 = context.Entry(subDependent1); + Assert.Equal(principal.Id, subDependentEntry1.Property("ParentId").CurrentValue); + Assert.Equal(useTrackGraph == null ? EntityState.Added : entityState, subDependentEntry1.State); Assert.Equal( typeof(Parent).ShortDisplayName() + "." @@ -319,10 +322,27 @@ public void Add_principal_with_dependent_both_navs(EntityState entityState, bool + "#" + nameof(Child) + "." - + nameof(Child.SubChild) + + nameof(Child.SubChild1) + "#" + nameof(SubChild), - subDependentEntry.Metadata.DisplayName()); + subDependentEntry1.Metadata.DisplayName()); + + Assert.Same(subDependent2, dependent.SubChild2); + Assert.Same(dependent, subDependent2.Parent); + var subDependentEntry2 = context.Entry(subDependent1); + Assert.Equal(principal.Id, subDependentEntry2.Property("ParentId").CurrentValue); + Assert.Equal(useTrackGraph == null ? EntityState.Added : entityState, subDependentEntry2.State); + Assert.Equal( + typeof(Parent).ShortDisplayName() + + "." + + nameof(Parent.Child1) + + "#" + + nameof(Child) + + "." + + nameof(Child.SubChild1) + + "#" + + nameof(SubChild), + subDependentEntry2.Metadata.DisplayName()); }); } @@ -348,8 +368,10 @@ public void Add_principal_with_dependent_principal_nav(EntityState entityState, var dependent = new Child { Name = "1" }; principal.Child1 = dependent; - var subDependent = new SubChild { Name = "1S" }; - dependent.SubChild = subDependent; + var subDependent1 = new SubChild { Name = "1S1" }; + dependent.SubChild1 = subDependent1; + var subDependent2 = new SubChild { Name = "1S2" }; + dependent.SubChild2 = subDependent2; if (useTrackGraph == null) { @@ -380,7 +402,7 @@ public void Add_principal_with_dependent_principal_nav(EntityState entityState, || useTrackGraph == null, context.ChangeTracker.HasChanges()); - Assert.Equal(3, context.ChangeTracker.Entries().Count()); + Assert.Equal(4, context.ChangeTracker.Entries().Count()); AssertFixup( context, @@ -392,11 +414,11 @@ public void Add_principal_with_dependent_principal_nav(EntityState entityState, Assert.Equal(entityState, context.Entry(principal).State); Assert.Equal(useTrackGraph == null ? EntityState.Added : entityState, context.Entry(dependent).State); - Assert.Same(subDependent, dependent.SubChild); - Assert.Same(dependent, subDependent.Parent); - var subDependentEntry = context.Entry(subDependent); - Assert.Equal(principal.Id, subDependentEntry.Property("ParentId").CurrentValue); - Assert.Equal(useTrackGraph == null ? EntityState.Added : entityState, subDependentEntry.State); + Assert.Same(subDependent1, dependent.SubChild1); + Assert.Same(dependent, subDependent1.Parent); + var subDependentEntry1 = context.Entry(subDependent1); + Assert.Equal(principal.Id, subDependentEntry1.Property("ParentId").CurrentValue); + Assert.Equal(useTrackGraph == null ? EntityState.Added : entityState, subDependentEntry1.State); Assert.Equal( typeof(Parent).ShortDisplayName() + "." @@ -404,10 +426,27 @@ public void Add_principal_with_dependent_principal_nav(EntityState entityState, + "#" + nameof(Child) + "." - + nameof(Child.SubChild) + + nameof(Child.SubChild1) + "#" - + nameof(Child.SubChild), - subDependentEntry.Metadata.DisplayName()); + + nameof(SubChild), + subDependentEntry1.Metadata.DisplayName()); + + Assert.Same(subDependent2, dependent.SubChild2); + Assert.Same(dependent, subDependent2.Parent); + var subDependentEntry2 = context.Entry(subDependent2); + Assert.Equal(principal.Id, subDependentEntry1.Property("ParentId").CurrentValue); + Assert.Equal(useTrackGraph == null ? EntityState.Added : entityState, subDependentEntry2.State); + Assert.Equal( + typeof(Parent).ShortDisplayName() + + "." + + nameof(Parent.Child1) + + "#" + + nameof(Child) + + "." + + nameof(Child.SubChild2) + + "#" + + nameof(SubChild), + subDependentEntry2.Metadata.DisplayName()); }); } @@ -975,8 +1014,10 @@ public void Instance_changed_bidirectional(EntityState entityState) var dependent1 = new Child { Name = "1" }; principal.Child1 = dependent1; - var subDependent1 = new SubChild { Name = "1S" }; - dependent1.SubChild = subDependent1; + var subDependent11 = new SubChild { Name = "1S1" }; + dependent1.SubChild1 = subDependent11; + var subDependent21 = new SubChild { Name = "1S2" }; + dependent1.SubChild2 = subDependent21; context.ChangeTracker.TrackGraph(principal, e => e.Entry.State = entityState); @@ -985,14 +1026,16 @@ public void Instance_changed_bidirectional(EntityState entityState) var dependent2 = new Child { Name = "2" }; principal.Child1 = dependent2; - var subDependent2 = new SubChild { Name = "2S" }; - dependent2.SubChild = subDependent2; + var subDependent12 = new SubChild { Name = "2S1" }; + dependent2.SubChild1 = subDependent12; + var subDependent22 = new SubChild { Name = "2S2" }; + dependent2.SubChild2 = subDependent22; context.ChangeTracker.DetectChanges(); Assert.True(context.ChangeTracker.HasChanges()); - Assert.Equal(entityState == EntityState.Added ? 3 : 5, context.ChangeTracker.Entries().Count()); + Assert.Equal(entityState == EntityState.Added ? 4 : 7, context.ChangeTracker.Entries().Count()); Assert.Null(principal.Child2); Assert.Same(principal, dependent2.Parent); Assert.Same(dependent2, principal.Child1); @@ -1005,11 +1048,11 @@ public void Instance_changed_bidirectional(EntityState entityState) typeof(Parent).ShortDisplayName() + "." + nameof(Parent.Child1) + "#" + nameof(Child), dependentEntry2.Metadata.DisplayName()); - Assert.Same(subDependent2, dependent2.SubChild); - Assert.Same(dependent2, subDependent2.Parent); - var subDependentEntry = dependentEntry2.Reference(p => p.SubChild).TargetEntry; - Assert.Equal(principal.Id, subDependentEntry.Property("ParentId").CurrentValue); - Assert.Equal(EntityState.Added, subDependentEntry.State); + Assert.Same(subDependent12, dependent2.SubChild1); + Assert.Same(dependent2, subDependent12.Parent); + var subDependentEntry1 = dependentEntry2.Reference(p => p.SubChild1).TargetEntry!; + Assert.Equal(principal.Id, subDependentEntry1.Property("ParentId").CurrentValue); + Assert.Equal(EntityState.Added, subDependentEntry1.State); Assert.Equal( typeof(Parent).ShortDisplayName() + "." @@ -1017,26 +1060,44 @@ public void Instance_changed_bidirectional(EntityState entityState) + "#" + nameof(Child) + "." - + nameof(ChildPN.SubChild) + + nameof(Child.SubChild1) + "#" - + nameof(SubChild), subDependentEntry.Metadata.DisplayName()); + + nameof(SubChild), subDependentEntry1.Metadata.DisplayName()); + + Assert.Same(subDependent22, dependent2.SubChild2); + Assert.Same(dependent2, subDependent22.Parent); + var subDependentEntry2 = dependentEntry2.Reference(p => p.SubChild2).TargetEntry!; + Assert.Equal(principal.Id, subDependentEntry2.Property("ParentId").CurrentValue); + Assert.Equal(EntityState.Added, subDependentEntry2.State); + Assert.Equal( + typeof(Parent).ShortDisplayName() + + "." + + nameof(Parent.Child1) + + "#" + + nameof(Child) + + "." + + nameof(Child.SubChild2) + + "#" + + nameof(SubChild), subDependentEntry2.Metadata.DisplayName()); context.ChangeTracker.CascadeChanges(); Assert.True(context.ChangeTracker.HasChanges()); - Assert.Equal(entityState == EntityState.Added ? 3 : 5, context.ChangeTracker.Entries().Count()); + Assert.Equal(entityState == EntityState.Added ? 4 : 7, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); Assert.False(context.ChangeTracker.HasChanges()); - Assert.Equal(3, context.ChangeTracker.Entries().Count()); + Assert.Equal(4, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Null(principal.Child2); Assert.Same(dependent2, principal.Child1); - Assert.Same(dependent2, subDependent2.Parent); - Assert.Same(subDependent2, dependent2.SubChild); + Assert.Same(dependent2, subDependent12.Parent); + Assert.Same(subDependent12, dependent2.SubChild1); + Assert.Same(dependent2, subDependent22.Parent); + Assert.Same(subDependent22, dependent2.SubChild2); } [ConditionalTheory] @@ -1349,8 +1410,10 @@ public void Identity_changed_bidirectional(EntityState entityState) var dependent = new Child { Name = "1" }; principal.Child2 = dependent; - var subDependent = new SubChild { Name = "1S" }; - dependent.SubChild = subDependent; + var subDependent1 = new SubChild { Name = "1S1" }; + dependent.SubChild1 = subDependent1; + var subDependent2 = new SubChild { Name = "1S2" }; + dependent.SubChild2 = subDependent2; context.ChangeTracker.TrackGraph(principal, e => e.Entry.State = entityState); @@ -1363,7 +1426,7 @@ public void Identity_changed_bidirectional(EntityState entityState) Assert.True(context.ChangeTracker.HasChanges()); - Assert.Equal(entityState == EntityState.Added ? 3 : 5, context.ChangeTracker.Entries().Count()); + Assert.Equal(entityState == EntityState.Added ? 4 : 7, context.ChangeTracker.Entries().Count()); Assert.Null(principal.Child2); Assert.Same(principal, dependent.Parent); Assert.Same(dependent, principal.Child1); @@ -1376,11 +1439,11 @@ public void Identity_changed_bidirectional(EntityState entityState) typeof(Parent).ShortDisplayName() + "." + nameof(Parent.Child1) + "#" + nameof(Child), dependentEntry2.Metadata.DisplayName()); - Assert.Same(subDependent, dependent.SubChild); - Assert.Same(dependent, subDependent.Parent); - var subDependentEntry = dependentEntry2.Reference(p => p.SubChild).TargetEntry; - Assert.Equal(principal.Id, subDependentEntry.Property("ParentId").CurrentValue); - Assert.Equal(EntityState.Added, subDependentEntry.State); + Assert.Same(subDependent1, dependent.SubChild1); + Assert.Same(dependent, subDependent1.Parent); + var subDependentEntry1 = dependentEntry2.Reference(p => p.SubChild1).TargetEntry!; + Assert.Equal(principal.Id, subDependentEntry1.Property("ParentId").CurrentValue); + Assert.Equal(EntityState.Added, subDependentEntry1.State); Assert.Equal( typeof(Parent).ShortDisplayName() + "." @@ -1388,26 +1451,44 @@ public void Identity_changed_bidirectional(EntityState entityState) + "#" + nameof(Child) + "." - + nameof(Child.SubChild) + + nameof(Child.SubChild1) + "#" - + nameof(SubChild), subDependentEntry.Metadata.DisplayName()); + + nameof(SubChild), subDependentEntry1.Metadata.DisplayName()); + + Assert.Same(subDependent2, dependent.SubChild2); + Assert.Same(dependent, subDependent1.Parent); + var subDependentEntry2 = dependentEntry2.Reference(p => p.SubChild2).TargetEntry!; + Assert.Equal(principal.Id, subDependentEntry1.Property("ParentId").CurrentValue); + Assert.Equal(EntityState.Added, subDependentEntry1.State); + Assert.Equal( + typeof(Parent).ShortDisplayName() + + "." + + nameof(Parent.Child1) + + "#" + + nameof(Child) + + "." + + nameof(Child.SubChild2) + + "#" + + nameof(SubChild), subDependentEntry2.Metadata.DisplayName()); context.ChangeTracker.CascadeChanges(); Assert.True(context.ChangeTracker.HasChanges()); - Assert.Equal(entityState == EntityState.Added ? 3 : 5, context.ChangeTracker.Entries().Count()); + Assert.Equal(entityState == EntityState.Added ? 4 : 7, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); Assert.False(context.ChangeTracker.HasChanges()); - Assert.Equal(3, context.ChangeTracker.Entries().Count()); + Assert.Equal(4, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Null(principal.Child2); Assert.Same(dependent, principal.Child1); - Assert.Same(subDependent, dependent.SubChild); - Assert.Same(dependent, subDependent.Parent); + Assert.Same(subDependent1, dependent.SubChild1); + Assert.Same(dependent, subDependent1.Parent); + Assert.Same(subDependent2, dependent.SubChild2); + Assert.Same(dependent, subDependent2.Parent); } [ConditionalTheory] @@ -1725,14 +1806,18 @@ public void Identity_swapped_bidirectional(EntityState entityState) var dependent1 = new Child { Name = "1" }; principal.Child1 = dependent1; - var subDependent1 = new SubChild { Name = "1S" }; - dependent1.SubChild = subDependent1; + var subDependent11 = new SubChild { Name = "1S1" }; + dependent1.SubChild1 = subDependent11; + var subDependent21 = new SubChild { Name = "1S2" }; + dependent1.SubChild2 = subDependent21; var dependent2 = new Child { Name = "2" }; principal.Child2 = dependent2; - var subDependent2 = new SubChild { Name = "2S" }; - dependent2.SubChild = subDependent2; + var subDependent12 = new SubChild { Name = "2S1" }; + dependent2.SubChild1 = subDependent12; + var subDependent22 = new SubChild { Name = "2S2" }; + dependent2.SubChild2 = subDependent22; context.ChangeTracker.TrackGraph(principal, e => e.Entry.State = entityState); @@ -1745,7 +1830,7 @@ public void Identity_swapped_bidirectional(EntityState entityState) Assert.True(context.ChangeTracker.HasChanges()); - Assert.Equal(entityState == EntityState.Added ? 5 : 9, context.ChangeTracker.Entries().Count()); + Assert.Equal(entityState == EntityState.Added ? 7 : 13, context.ChangeTracker.Entries().Count()); Assert.Same(principal, dependent1.Parent); Assert.Same(dependent1, principal.Child2); Assert.Same(principal, dependent2.Parent); @@ -1772,11 +1857,11 @@ public void Identity_swapped_bidirectional(EntityState entityState) entityState == EntityState.Added ? null : EntityState.Deleted, dependent2Entry.GetInfrastructure().SharedIdentityEntry?.EntityState); - Assert.Same(subDependent1, dependent1.SubChild); - Assert.Same(dependent1, subDependent1.Parent); - var subDependentEntry1 = dependent1Entry.Reference(p => p.SubChild).TargetEntry; - Assert.Equal(principal.Id, subDependentEntry1.Property("ParentId").CurrentValue); - Assert.Equal(EntityState.Added, subDependentEntry1.State); + Assert.Same(subDependent11, dependent1.SubChild1); + Assert.Same(dependent1, subDependent11.Parent); + var subDependentEntry11 = dependent1Entry.Reference(p => p.SubChild1).TargetEntry!; + Assert.Equal(principal.Id, subDependentEntry11.Property("ParentId").CurrentValue); + Assert.Equal(EntityState.Added, subDependentEntry11.State); Assert.Equal( typeof(Parent).ShortDisplayName() + "." @@ -1784,15 +1869,15 @@ public void Identity_swapped_bidirectional(EntityState entityState) + "#" + nameof(Child) + "." - + nameof(Child.SubChild) + + nameof(Child.SubChild1) + "#" - + nameof(SubChild), subDependentEntry1.Metadata.DisplayName()); + + nameof(SubChild), subDependentEntry11.Metadata.DisplayName()); - Assert.Same(subDependent2, dependent2.SubChild); - Assert.Same(dependent2, subDependent2.Parent); - var subDependentEntry2 = dependent1Entry.Reference(p => p.SubChild).TargetEntry; - Assert.Equal(principal.Id, subDependentEntry2.Property("ParentId").CurrentValue); - Assert.Equal(EntityState.Added, subDependentEntry2.State); + Assert.Same(subDependent21, dependent1.SubChild2); + Assert.Same(dependent1, subDependent21.Parent); + var subDependentEntry21 = dependent1Entry.Reference(p => p.SubChild2).TargetEntry!; + Assert.Equal(principal.Id, subDependentEntry21.Property("ParentId").CurrentValue); + Assert.Equal(EntityState.Added, subDependentEntry11.State); Assert.Equal( typeof(Parent).ShortDisplayName() + "." @@ -1800,28 +1885,62 @@ public void Identity_swapped_bidirectional(EntityState entityState) + "#" + nameof(Child) + "." - + nameof(Child.SubChild) + + nameof(Child.SubChild2) + "#" - + nameof(SubChild), subDependentEntry2.Metadata.DisplayName()); + + nameof(SubChild), subDependentEntry21.Metadata.DisplayName()); + + Assert.Same(subDependent12, dependent2.SubChild1); + Assert.Same(dependent2, subDependent12.Parent); + var subDependentEntry12 = dependent1Entry.Reference(p => p.SubChild1).TargetEntry!; + Assert.Equal(principal.Id, subDependentEntry12.Property("ParentId").CurrentValue); + Assert.Equal(EntityState.Added, subDependentEntry12.State); + Assert.Equal( + typeof(Parent).ShortDisplayName() + + "." + + nameof(Parent.Child1) + + "#" + + nameof(Child) + + "." + + nameof(Child.SubChild1) + + "#" + + nameof(SubChild), subDependentEntry12.Metadata.DisplayName()); + + Assert.Same(subDependent22, dependent2.SubChild2); + Assert.Same(dependent2, subDependent22.Parent); + var subDependentEntry22 = dependent1Entry.Reference(p => p.SubChild2).TargetEntry!; + Assert.Equal(principal.Id, subDependentEntry12.Property("ParentId").CurrentValue); + Assert.Equal(EntityState.Added, subDependentEntry22.State); + Assert.Equal( + typeof(Parent).ShortDisplayName() + + "." + + nameof(Parent.Child1) + + "#" + + nameof(Child) + + "." + + nameof(Child.SubChild2) + + "#" + + nameof(SubChild), subDependentEntry22.Metadata.DisplayName()); context.ChangeTracker.CascadeChanges(); Assert.True(context.ChangeTracker.HasChanges()); - Assert.Equal(entityState == EntityState.Added ? 5 : 9, context.ChangeTracker.Entries().Count()); + Assert.Equal(entityState == EntityState.Added ? 7 : 13, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); Assert.False(context.ChangeTracker.HasChanges()); - Assert.Equal(5, context.ChangeTracker.Entries().Count()); + Assert.Equal(7, context.ChangeTracker.Entries().Count()); Assert.Null(dependent1Entry.GetInfrastructure().SharedIdentityEntry); Assert.Null(dependent2Entry.GetInfrastructure().SharedIdentityEntry); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Same(dependent1, principal.Child2); Assert.Same(dependent2, principal.Child1); - Assert.Same(subDependent1, dependent1.SubChild); - Assert.Same(subDependent2, dependent2.SubChild); + Assert.Same(subDependent11, dependent1.SubChild1); + Assert.Same(subDependent12, dependent2.SubChild1); + Assert.Same(subDependent21, dependent1.SubChild2); + Assert.Same(subDependent22, dependent2.SubChild2); } [ConditionalTheory] @@ -2093,7 +2212,7 @@ public void Identity_swapped_bidirectional_collection(EntityState entityState, C + "." + nameof(Child.SubChildCollection) + "#" - + nameof(Child.SubChild), newSubDependentEntry1.Metadata.DisplayName()); + + nameof(SubChild), newSubDependentEntry1.Metadata.DisplayName()); Assert.Contains(dependent2.SubChildCollection, e => ReferenceEquals(e, subDependent2)); Assert.Same(dependent2, subDependent2.Parent); @@ -2108,7 +2227,7 @@ public void Identity_swapped_bidirectional_collection(EntityState entityState, C + "." + nameof(Child.SubChildCollection) + "#" - + nameof(Child.SubChild), newSubDependentEntry2.Metadata.DisplayName()); + + nameof(SubChild), newSubDependentEntry2.Metadata.DisplayName()); context.ChangeTracker.CascadeChanges(); @@ -2235,8 +2354,10 @@ public void Parent_changed_bidirectional(EntityState entityState) var dependent = new Child { Name = "1" }; principal1.Child1 = dependent; - var subDependent = new SubChild { Name = "1S" }; - dependent.SubChild = subDependent; + var subDependent1 = new SubChild { Name = "1S1" }; + dependent.SubChild1 = subDependent1; + var subDependent2 = new SubChild { Name = "1S2" }; + dependent.SubChild2 = subDependent2; context.ChangeTracker.TrackGraph(principal1, e => e.Entry.State = entityState); context.ChangeTracker.TrackGraph(principal2, e => e.Entry.State = entityState); @@ -2260,7 +2381,7 @@ public void Parent_changed_bidirectional(EntityState entityState) Assert.True(context.ChangeTracker.HasChanges()); - Assert.Equal(4, context.ChangeTracker.Entries().Count()); + Assert.Equal(5, context.ChangeTracker.Entries().Count()); Assert.Null(principal1.Child1); Assert.Null(principal1.Child2); Assert.Same(dependent, principal2.Child1); @@ -2276,11 +2397,11 @@ public void Parent_changed_bidirectional(EntityState entityState) typeof(Parent).ShortDisplayName() + "." + nameof(Parent.Child1) + "#" + nameof(Child), dependentEntry2.Metadata.DisplayName()); - Assert.Same(subDependent, dependent.SubChild); - Assert.Same(dependent, subDependent.Parent); - var subDependentEntry = dependentEntry2.Reference(p => p.SubChild).TargetEntry; - Assert.Equal(principal2.Id, subDependentEntry.Property("ParentId").CurrentValue); - Assert.Equal(EntityState.Added, subDependentEntry.State); + Assert.Same(subDependent1, dependent.SubChild1); + Assert.Same(dependent, subDependent1.Parent); + var subDependentEntry1 = dependentEntry2.Reference(p => p.SubChild1).TargetEntry!; + Assert.Equal(principal2.Id, subDependentEntry1.Property("ParentId").CurrentValue); + Assert.Equal(EntityState.Added, subDependentEntry1.State); Assert.Equal( typeof(Parent).ShortDisplayName() + "." @@ -2288,29 +2409,48 @@ public void Parent_changed_bidirectional(EntityState entityState) + "#" + nameof(Child) + "." - + nameof(ChildPN.SubChild) + + nameof(Child.SubChild1) + "#" + nameof(SubChild), - subDependentEntry.Metadata.DisplayName()); + subDependentEntry1.Metadata.DisplayName()); + + Assert.Same(subDependent2, dependent.SubChild2); + Assert.Same(dependent, subDependent1.Parent); + var subDependentEntry2 = dependentEntry2.Reference(p => p.SubChild2).TargetEntry!; + Assert.Equal(principal2.Id, subDependentEntry2.Property("ParentId").CurrentValue); + Assert.Equal(EntityState.Added, subDependentEntry2.State); + Assert.Equal( + typeof(Parent).ShortDisplayName() + + "." + + nameof(Parent.Child1) + + "#" + + nameof(Child) + + "." + + nameof(Child.SubChild2) + + "#" + + nameof(SubChild), + subDependentEntry2.Metadata.DisplayName()); context.ChangeTracker.CascadeChanges(); Assert.True(context.ChangeTracker.HasChanges()); - Assert.Equal(4, context.ChangeTracker.Entries().Count()); + Assert.Equal(5, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); Assert.False(context.ChangeTracker.HasChanges()); - Assert.Equal(4, context.ChangeTracker.Entries().Count()); + Assert.Equal(5, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Null(principal1.Child1); Assert.Null(principal1.Child2); Assert.Same(dependent, principal2.Child1); Assert.Null(principal2.Child2); - Assert.Same(subDependent, dependent.SubChild); - Assert.Same(dependent, subDependent.Parent); + Assert.Same(subDependent1, dependent.SubChild1); + Assert.Same(dependent, subDependent1.Parent); + Assert.Same(subDependent2, dependent.SubChild2); + Assert.Same(dependent, subDependent2.Parent); } } @@ -2694,14 +2834,18 @@ public void Parent_swapped_bidirectional(EntityState entityState) var dependent1 = new Child { Name = "1" }; principal1.Child1 = dependent1; - var subDependent1 = new SubChild { Name = "1S" }; - dependent1.SubChild = subDependent1; + var subDependent11 = new SubChild { Name = "1S11" }; + dependent1.SubChild1 = subDependent11; + var subDependent21 = new SubChild { Name = "1S21" }; + dependent1.SubChild2 = subDependent21; var dependent2 = new Child { Name = "2" }; principal2.Child1 = dependent2; - var subDependent2 = new SubChild { Name = "2S" }; - dependent2.SubChild = subDependent2; + var subDependent12 = new SubChild { Name = "2S12" }; + dependent2.SubChild1 = subDependent12; + var subDependent22 = new SubChild { Name = "2S22" }; + dependent2.SubChild2 = subDependent22; context.ChangeTracker.TrackGraph(principal1, e => e.Entry.State = entityState); context.ChangeTracker.TrackGraph(principal2, e => e.Entry.State = entityState); @@ -2725,7 +2869,7 @@ public void Parent_swapped_bidirectional(EntityState entityState) Assert.True(context.ChangeTracker.HasChanges()); - Assert.Equal(6, context.ChangeTracker.Entries().Count()); + Assert.Equal(8, context.ChangeTracker.Entries().Count()); Assert.Same(dependent2, principal1.Child1); Assert.Null(principal1.Child2); Assert.Same(dependent1, principal2.Child1); @@ -2749,11 +2893,11 @@ public void Parent_swapped_bidirectional(EntityState entityState) typeof(Parent).ShortDisplayName() + "." + nameof(Parent.Child1) + "#" + nameof(Child), dependent2Entry.Metadata.DisplayName()); - Assert.Same(subDependent1, dependent1.SubChild); - Assert.Same(dependent1, subDependent1.Parent); - var subDependentEntry1 = dependent1Entry.Reference(p => p.SubChild).TargetEntry; - Assert.Equal(principal1.Id, subDependentEntry1.Property("ParentId").CurrentValue); - Assert.Equal(EntityState.Added, subDependentEntry1.State); + Assert.Same(subDependent11, dependent1.SubChild1); + Assert.Same(dependent1, subDependent11.Parent); + var subDependentEntry11 = dependent1Entry.Reference(p => p.SubChild1).TargetEntry!; + Assert.Equal(principal1.Id, subDependentEntry11.Property("ParentId").CurrentValue); + Assert.Equal(EntityState.Added, subDependentEntry11.State); Assert.Equal( typeof(Parent).ShortDisplayName() + "." @@ -2761,16 +2905,16 @@ public void Parent_swapped_bidirectional(EntityState entityState) + "#" + nameof(Child) + "." - + nameof(Child.SubChild) + + nameof(Child.SubChild1) + "#" - + nameof(Child.SubChild), - subDependentEntry1.Metadata.DisplayName()); + + nameof(SubChild), + subDependentEntry11.Metadata.DisplayName()); - Assert.Same(subDependent2, dependent2.SubChild); - Assert.Same(dependent2, subDependent2.Parent); - var subDependentEntry2 = dependent2Entry.Reference(p => p.SubChild).TargetEntry; - Assert.Equal(principal2.Id, subDependentEntry2.Property("ParentId").CurrentValue); - Assert.Equal(EntityState.Added, subDependentEntry2.State); + Assert.Same(subDependent21, dependent1.SubChild2); + Assert.Same(dependent1, subDependent21.Parent); + var subDependentEntry21 = dependent1Entry.Reference(p => p.SubChild2).TargetEntry!; + Assert.Equal(principal1.Id, subDependentEntry21.Property("ParentId").CurrentValue); + Assert.Equal(EntityState.Added, subDependentEntry21.State); Assert.Equal( typeof(Parent).ShortDisplayName() + "." @@ -2778,31 +2922,69 @@ public void Parent_swapped_bidirectional(EntityState entityState) + "#" + nameof(Child) + "." - + nameof(Child.SubChild) + + nameof(Child.SubChild2) + "#" - + nameof(Child.SubChild), - subDependentEntry2.Metadata.DisplayName()); + + nameof(SubChild), + subDependentEntry21.Metadata.DisplayName()); + + Assert.Same(subDependent12, dependent2.SubChild1); + Assert.Same(dependent2, subDependent12.Parent); + var subDependentEntry12 = dependent2Entry.Reference(p => p.SubChild1).TargetEntry!; + Assert.Equal(principal2.Id, subDependentEntry12.Property("ParentId").CurrentValue); + Assert.Equal(EntityState.Added, subDependentEntry12.State); + Assert.Equal( + typeof(Parent).ShortDisplayName() + + "." + + nameof(Parent.Child1) + + "#" + + nameof(Child) + + "." + + nameof(Child.SubChild1) + + "#" + + nameof(SubChild), + subDependentEntry12.Metadata.DisplayName()); + + Assert.Same(subDependent22, dependent2.SubChild2); + Assert.Same(dependent2, subDependent22.Parent); + var subDependentEntry22 = dependent2Entry.Reference(p => p.SubChild2).TargetEntry!; + Assert.Equal(principal2.Id, subDependentEntry22.Property("ParentId").CurrentValue); + Assert.Equal(EntityState.Added, subDependentEntry22.State); + Assert.Equal( + typeof(Parent).ShortDisplayName() + + "." + + nameof(Parent.Child1) + + "#" + + nameof(Child) + + "." + + nameof(Child.SubChild2) + + "#" + + nameof(SubChild), + subDependentEntry22.Metadata.DisplayName()); context.ChangeTracker.CascadeChanges(); Assert.True(context.ChangeTracker.HasChanges()); - Assert.Equal(6, context.ChangeTracker.Entries().Count()); + Assert.Equal(8, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); Assert.False(context.ChangeTracker.HasChanges()); - Assert.Equal(6, context.ChangeTracker.Entries().Count()); + Assert.Equal(8, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Same(dependent2, principal1.Child1); Assert.Null(principal1.Child2); Assert.Same(dependent1, principal2.Child1); Assert.Null(principal2.Child2); - Assert.Same(subDependent1, dependent1.SubChild); - Assert.Same(subDependent2, dependent2.SubChild); - Assert.Same(dependent1, subDependent1.Parent); - Assert.Same(dependent2, subDependent2.Parent); + Assert.Same(subDependent11, dependent1.SubChild1); + Assert.Same(subDependent12, dependent2.SubChild1); + Assert.Same(dependent1, subDependent11.Parent); + Assert.Same(dependent2, subDependent12.Parent); + Assert.Same(subDependent21, dependent1.SubChild2); + Assert.Same(subDependent22, dependent2.SubChild2); + Assert.Same(dependent1, subDependent21.Parent); + Assert.Same(dependent2, subDependent22.Parent); } } @@ -3078,7 +3260,7 @@ public void Parent_swapped_bidirectional_collection(EntityState entityState, Col + "." + nameof(Child.SubChildCollection) + "#" - + nameof(Child.SubChild), + + nameof(SubChild), newSubDependentEntry1.Metadata.DisplayName()); Assert.Contains(dependent2.SubChildCollection, e => ReferenceEquals(e, subDependent2)); @@ -3096,7 +3278,7 @@ public void Parent_swapped_bidirectional_collection(EntityState entityState, Col + "." + nameof(Child.SubChildCollection) + "#" - + nameof(Child.SubChild), + + nameof(SubChild), newSubDependentEntry2.Metadata.DisplayName()); context.ChangeTracker.CascadeChanges(); @@ -3217,8 +3399,11 @@ public void Parent_and_identity_changed_bidirectional(EntityState entityState) var dependent = new Child { Name = "1" }; principal1.Child2 = dependent; - var subDependent = new SubChild { Name = "1S" }; - dependent.SubChild = subDependent; + var subDependent1 = new SubChild { Name = "1S1" }; + dependent.SubChild1 = subDependent1; + + var subDependent2 = new SubChild { Name = "1S2" }; + dependent.SubChild2 = subDependent2; context.ChangeTracker.TrackGraph(principal1, e => e.Entry.State = entityState); context.ChangeTracker.TrackGraph(principal2, e => e.Entry.State = entityState); @@ -3242,7 +3427,7 @@ public void Parent_and_identity_changed_bidirectional(EntityState entityState) Assert.True(context.ChangeTracker.HasChanges()); - Assert.Equal(entityState == EntityState.Added ? 4 : 6, context.ChangeTracker.Entries().Count()); + Assert.Equal(5, context.ChangeTracker.Entries().Count()); Assert.Null(principal1.Child1); Assert.Null(principal1.Child2); Assert.Same(dependent, principal2.Child1); @@ -3250,7 +3435,7 @@ public void Parent_and_identity_changed_bidirectional(EntityState entityState) Assert.Same(principal2, dependent.Parent); Assert.Equal(entityState, context.Entry(principal1).State); Assert.Equal(entityState, context.Entry(principal2).State); - Assert.Equal(entityState == EntityState.Added ? EntityState.Detached : EntityState.Deleted, dependentEntry1.State); + Assert.Equal(EntityState.Detached, dependentEntry1.State); var dependentEntry2 = context.Entry(principal2).Reference(p => p.Child1).TargetEntry; Assert.Equal(principal2.Id, dependentEntry2.Property("ParentId").CurrentValue); Assert.Equal(EntityState.Added, dependentEntry2.State); @@ -3258,11 +3443,11 @@ public void Parent_and_identity_changed_bidirectional(EntityState entityState) typeof(Parent).ShortDisplayName() + "." + nameof(Parent.Child1) + "#" + nameof(Child), dependentEntry2.Metadata.DisplayName()); - Assert.Same(subDependent, dependent.SubChild); - Assert.Same(dependent, subDependent.Parent); - var subDependentEntry = dependentEntry2.Reference(p => p.SubChild).TargetEntry; - Assert.Equal(principal2.Id, subDependentEntry.Property("ParentId").CurrentValue); - Assert.Equal(EntityState.Added, subDependentEntry.State); + Assert.Same(subDependent1, dependent.SubChild1); + Assert.Same(dependent, subDependent1.Parent); + var subDependentEntry1 = dependentEntry2.Reference(p => p.SubChild1).TargetEntry!; + Assert.Equal(principal2.Id, subDependentEntry1.Property("ParentId").CurrentValue); + Assert.Equal(EntityState.Added, subDependentEntry1.State); Assert.Equal( typeof(Parent).ShortDisplayName() + "." @@ -3270,29 +3455,48 @@ public void Parent_and_identity_changed_bidirectional(EntityState entityState) + "#" + nameof(Child) + "." - + nameof(ChildPN.SubChild) + + nameof(Child.SubChild1) + "#" + nameof(SubChild), - subDependentEntry.Metadata.DisplayName()); + subDependentEntry1.Metadata.DisplayName()); + + Assert.Same(subDependent2, dependent.SubChild2); + Assert.Same(dependent, subDependent1.Parent); + var subDependentEntry2 = dependentEntry2.Reference(p => p.SubChild2).TargetEntry!; + Assert.Equal(principal2.Id, subDependentEntry2.Property("ParentId").CurrentValue); + Assert.Equal(EntityState.Added, subDependentEntry2.State); + Assert.Equal( + typeof(Parent).ShortDisplayName() + + "." + + nameof(Parent.Child1) + + "#" + + nameof(Child) + + "." + + nameof(Child.SubChild2) + + "#" + + nameof(SubChild), + subDependentEntry2.Metadata.DisplayName()); context.ChangeTracker.CascadeChanges(); Assert.True(context.ChangeTracker.HasChanges()); - Assert.Equal(entityState == EntityState.Added ? 4 : 6, context.ChangeTracker.Entries().Count()); + Assert.Equal(5, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); Assert.False(context.ChangeTracker.HasChanges()); - Assert.Equal(4, context.ChangeTracker.Entries().Count()); + Assert.Equal(5, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Null(principal1.Child1); Assert.Null(principal1.Child2); Assert.Same(dependent, principal2.Child1); Assert.Null(principal2.Child2); - Assert.Same(subDependent, dependent.SubChild); - Assert.Same(dependent, subDependent.Parent); + Assert.Same(subDependent1, dependent.SubChild1); + Assert.Same(dependent, subDependent1.Parent); + Assert.Same(subDependent2, dependent.SubChild2); + Assert.Same(dependent, subDependent2.Parent); } [ConditionalTheory] @@ -3653,13 +3857,13 @@ public void Parent_and_identity_swapped_bidirectional(EntityState entityState) principal1.Child2 = dependent1; var subDependent1 = new SubChild { Name = "1S" }; - dependent1.SubChild = subDependent1; + dependent1.SubChild1 = subDependent1; var dependent2 = new Child { Name = "2" }; principal2.Child1 = dependent2; var subDependent2 = new SubChild { Name = "2S" }; - dependent2.SubChild = subDependent2; + dependent2.SubChild1 = subDependent2; context.ChangeTracker.TrackGraph(principal1, e => e.Entry.State = entityState); context.ChangeTracker.TrackGraph(principal2, e => e.Entry.State = entityState); @@ -3703,9 +3907,9 @@ public void Parent_and_identity_swapped_bidirectional(EntityState entityState) entityState == EntityState.Added ? null : EntityState.Deleted, dependent1Entry.GetInfrastructure().SharedIdentityEntry?.EntityState); - Assert.Same(subDependent1, dependent1.SubChild); + Assert.Same(subDependent1, dependent1.SubChild1); Assert.Same(dependent1, subDependent1.Parent); - var subDependentEntry1 = dependent1Entry.Reference(p => p.SubChild).TargetEntry; + var subDependentEntry1 = dependent1Entry.Reference(p => p.SubChild1).TargetEntry; Assert.Equal(principal1.Id, subDependentEntry1.Property("ParentId").CurrentValue); Assert.Equal(EntityState.Added, subDependentEntry1.State); Assert.Equal( @@ -3715,13 +3919,13 @@ public void Parent_and_identity_swapped_bidirectional(EntityState entityState) + "#" + nameof(Child) + "." - + nameof(Child.SubChild) + + nameof(Child.SubChild1) + "#" - + nameof(Child.SubChild), subDependentEntry1.Metadata.DisplayName()); + + nameof(SubChild), subDependentEntry1.Metadata.DisplayName()); - Assert.Same(subDependent2, dependent2.SubChild); + Assert.Same(subDependent2, dependent2.SubChild1); Assert.Same(dependent2, subDependent2.Parent); - var subDependentEntry2 = dependent2Entry.Reference(p => p.SubChild).TargetEntry; + var subDependentEntry2 = dependent2Entry.Reference(p => p.SubChild1).TargetEntry; Assert.Equal(principal2.Id, subDependentEntry2.Property("ParentId").CurrentValue); Assert.Equal(EntityState.Added, subDependentEntry2.State); Assert.Equal( @@ -3731,9 +3935,9 @@ public void Parent_and_identity_swapped_bidirectional(EntityState entityState) + "#" + nameof(Child) + "." - + nameof(Child.SubChild) + + nameof(Child.SubChild1) + "#" - + nameof(Child.SubChild), subDependentEntry2.Metadata.DisplayName()); + + nameof(SubChild), subDependentEntry2.Metadata.DisplayName()); context.ChangeTracker.CascadeChanges(); @@ -3753,8 +3957,8 @@ public void Parent_and_identity_swapped_bidirectional(EntityState entityState) Assert.Same(dependent2, principal1.Child2); Assert.Same(dependent1, principal2.Child1); Assert.Null(principal2.Child2); - Assert.Same(subDependent1, dependent1.SubChild); - Assert.Same(subDependent2, dependent2.SubChild); + Assert.Same(subDependent1, dependent1.SubChild1); + Assert.Same(subDependent2, dependent2.SubChild1); Assert.Same(dependent1, subDependent1.Parent); Assert.Same(dependent2, subDependent2.Parent); } @@ -4056,7 +4260,7 @@ public void Parent_and_identity_swapped_bidirectional_collection(EntityState ent + "." + nameof(Child.SubChildCollection) + "#" - + nameof(Child.SubChild), newSubDependentEntry2.Metadata.DisplayName()); + + nameof(SubChild), newSubDependentEntry2.Metadata.DisplayName()); Assert.Contains(dependent2.SubChildCollection, e => ReferenceEquals(e, subDependent2)); Assert.Same(dependent2, subDependent2.Parent); @@ -4071,7 +4275,7 @@ public void Parent_and_identity_swapped_bidirectional_collection(EntityState ent + "." + nameof(Child.SubChildCollection) + "#" - + nameof(Child.SubChild), newSubDependentEntry1.Metadata.DisplayName()); + + nameof(SubChild), newSubDependentEntry1.Metadata.DisplayName()); context.ChangeTracker.CascadeChanges(); @@ -4097,6 +4301,93 @@ public void Parent_and_identity_swapped_bidirectional_collection(EntityState ent Assert.Same(dependent2, subDependent2.Parent); } + + [ConditionalTheory] + [InlineData(EntityState.Added, true, true, true)] + [InlineData(EntityState.Modified, true, true, true)] + [InlineData(EntityState.Unchanged, true, true, true)] + [InlineData(EntityState.Added, true, true, false)] + [InlineData(EntityState.Modified, true, true, false)] + [InlineData(EntityState.Unchanged, true, true, false)] + [InlineData(EntityState.Added, true, false, true)] + [InlineData(EntityState.Modified, true, false, true)] + [InlineData(EntityState.Unchanged, true, false, true)] + [InlineData(EntityState.Added, true, false, false)] + [InlineData(EntityState.Modified, true, false, false)] + [InlineData(EntityState.Unchanged, true, false, false)] + [InlineData(EntityState.Added, false, true, true)] + [InlineData(EntityState.Modified, false, true, true)] + [InlineData(EntityState.Unchanged, false, true, true)] + [InlineData(EntityState.Added, false, true, false)] + [InlineData(EntityState.Modified, false, true, false)] + [InlineData(EntityState.Unchanged, false, true, false)] + [InlineData(EntityState.Added, false, false, true)] + [InlineData(EntityState.Modified, false, false, true)] + [InlineData(EntityState.Unchanged, false, false, true)] + [InlineData(EntityState.Added, false, false, false)] + [InlineData(EntityState.Modified, false, false, false)] + [InlineData(EntityState.Unchanged, false, false, false)] + public void Can_set_nested_dependent_to_null(EntityState entityState, bool null1, bool null2, bool nullC) + { + using var context = new FixupContext(); + + var subChild1 = new SubChild { Name = "1S1" }; + var subChild2 = new SubChild { Name = "1S2" }; + var subChild3 = new SubChild { Name = "1S3" }; + var subChildCollection = new List { subChild3 }; + var child = new Child + { + Name = "1", + SubChild1 = subChild1, + SubChild2 = subChild2, + SubChildCollection = subChildCollection + }; + var principal = new Parent { Id = 77, Child1 = child }; + + _ = entityState switch + { + EntityState.Added => context.Add(principal), + EntityState.Unchanged => context.Attach(principal), + EntityState.Modified => context.Update(principal), + _ => throw new ArgumentOutOfRangeException() + }; + + var newSubChild1 = new SubChild { Name = "n1S1" }; + var newSubChild2 = new SubChild { Name = "n1S2" }; + var newSubChild3 = new SubChild { Name = "n1S3" }; + var newSubChildCollection = new List { newSubChild3 }; + var newChild = new Child + { + Name = "n1", + SubChild1 = null1 ? null : newSubChild1, + SubChild2 = null2 ? null : newSubChild2, + SubChildCollection = nullC ? null : newSubChildCollection + }; + + principal.Child1 = newChild; + + context.ChangeTracker.DetectChanges(); + AssertChildren(); + + context.ChangeTracker.DetectChanges(); + AssertChildren(); + + void AssertChildren() + { + Assert.Equal("n1", newChild.Name); + Assert.Equal("n1S1", newSubChild1.Name); + Assert.Equal("n1S2", newSubChild2.Name); + Assert.Equal("n1S3", newSubChild3.Name); + Assert.Single(newSubChildCollection); + + Assert.Same(newChild, principal.Child1); + Assert.Same(null1 ? null : newSubChild1, newChild.SubChild1); + Assert.Same(null2 ? null : newSubChild2, newChild.SubChild2); + Assert.Same(nullC ? null : newSubChildCollection, newChild.SubChildCollection); + Assert.Same(nullC ? null : newSubChildCollection.Single(), newChild.SubChildCollection?.Single()); + } + } + [ConditionalTheory] [InlineData(false)] [InlineData(true)] @@ -4891,7 +5182,8 @@ private class Child : IComparable public string Name { get; set; } public Parent Parent { get; set; } - public SubChild SubChild { get; set; } + public SubChild SubChild1 { get; set; } + public SubChild SubChild2 { get; set; } public ICollection SubChildCollection { get; set; } public int CompareTo(Child other) @@ -5017,7 +5309,15 @@ protected internal override void OnModelCreating(ModelBuilder modelBuilder) .HasForeignKey("ParentId"); cb.OwnsOne( - c => c.SubChild, sb => + c => c.SubChild1, sb => + { + sb.Property("ParentId"); + sb.WithOwner(c => c.Parent) + .HasForeignKey("ParentId"); + }); + + cb.OwnsOne( + c => c.SubChild2, sb => { sb.Property("ParentId"); sb.WithOwner(c => c.Parent) @@ -5041,7 +5341,15 @@ protected internal override void OnModelCreating(ModelBuilder modelBuilder) .HasForeignKey("ParentId"); cb.OwnsOne( - c => c.SubChild, sb => + c => c.SubChild1, sb => + { + sb.Property("ParentId"); + sb.WithOwner(c => c.Parent) + .HasForeignKey("ParentId"); + }); + + cb.OwnsOne( + c => c.SubChild2, sb => { sb.Property("ParentId"); sb.WithOwner(c => c.Parent) @@ -5065,7 +5373,16 @@ protected internal override void OnModelCreating(ModelBuilder modelBuilder) .HasForeignKey("ParentId"); cb.OwnsOne( - c => c.SubChild, sb => + c => c.SubChild1, sb => + { + sb.Property("ParentId"); + sb.Property("ChildId"); + sb.WithOwner(c => c.Parent) + .HasForeignKey("ParentId", "ChildId"); + }); + + cb.OwnsOne( + c => c.SubChild2, sb => { sb.Property("ParentId"); sb.Property("ChildId"); @@ -5091,7 +5408,16 @@ protected internal override void OnModelCreating(ModelBuilder modelBuilder) .HasForeignKey("ParentId"); cb.OwnsOne( - c => c.SubChild, sb => + c => c.SubChild1, sb => + { + sb.Property("ParentId"); + sb.Property("ChildId"); + sb.WithOwner(c => c.Parent) + .HasForeignKey("ParentId", "ChildId"); + }); + + cb.OwnsOne( + c => c.SubChild2, sb => { sb.Property("ParentId"); sb.Property("ChildId");