diff --git a/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs b/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs index 49eed26f449..53889226c77 100644 --- a/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs +++ b/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs @@ -325,6 +325,11 @@ private void SetEntityState(EntityState oldState, EntityState newState, bool acc FireStateChanged(oldState); + if (newState == EntityState.Unchanged) + { + SharedIdentityEntry?.SetEntityState(EntityState.Detached); + } + if ((newState == EntityState.Deleted || newState == EntityState.Detached) && StateManager.CascadeDeleteTiming == CascadeTiming.Immediate) diff --git a/test/EFCore.Specification.Tests/GraphUpdatesTestBase.cs b/test/EFCore.Specification.Tests/GraphUpdatesTestBase.cs index 072f313963f..d4b7ca7460a 100644 --- a/test/EFCore.Specification.Tests/GraphUpdatesTestBase.cs +++ b/test/EFCore.Specification.Tests/GraphUpdatesTestBase.cs @@ -63,13 +63,18 @@ public virtual void Changes_to_Added_relationships_are_picked_up(ChangeMechanism Assert.Null(entity.RootId); Assert.Null(entity.Root); + Assert.True(context.ChangeTracker.HasChanges()); + context.SaveChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + id = entity.Id; }, context => { var entity = context.Set().Include(e => e.Root).Single(e => e.Id == id); + Assert.Null(entity.Root); Assert.Null(entity.RootId); }); @@ -111,6 +116,8 @@ public virtual void New_FK_is_not_cleared_on_old_dependent_delete( context.Remove(removed); + Assert.True(context.ChangeTracker.HasChanges()); + if (Fixture.ForceClientNoAction) { Assert.Throws(() => context.SaveChanges()); @@ -119,6 +126,8 @@ public virtual void New_FK_is_not_cleared_on_old_dependent_delete( { context.SaveChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(EntityState.Detached, context.Entry(removed).State); Assert.Equal(newFk, child.ParentId); @@ -154,6 +163,8 @@ public virtual void New_FK_is_not_cleared_on_old_dependent_delete( { Assert.Null((child.Parent)); } + + Assert.False(context.ChangeTracker.HasChanges()); } }); } @@ -173,8 +184,12 @@ public virtual void Optional_One_to_one_relationships_are_one_to_one( var root = context.Set().Single(IsTheRoot); + Assert.False(context.ChangeTracker.HasChanges()); + root.OptionalSingle = new OptionalSingle1(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Throws(() => context.SaveChanges()); }); } @@ -194,8 +209,12 @@ public virtual void Required_One_to_one_relationships_are_one_to_one( var root = context.Set().Single(IsTheRoot); + Assert.False(context.ChangeTracker.HasChanges()); + root.RequiredSingle = new RequiredSingle1(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Throws(() => context.SaveChanges()); }); } @@ -215,8 +234,12 @@ public virtual void Optional_One_to_one_with_AK_relationships_are_one_to_one( var root = context.Set().Single(IsTheRoot); + Assert.False(context.ChangeTracker.HasChanges()); + root.OptionalSingleAk = new OptionalSingleAk1(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Throws(() => context.SaveChanges()); }); } @@ -236,8 +259,12 @@ public virtual void Required_One_to_one_with_AK_relationships_are_one_to_one( var root = context.Set().Single(IsTheRoot); + Assert.False(context.ChangeTracker.HasChanges()); + root.RequiredSingleAk = new RequiredSingleAk1(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Throws(() => context.SaveChanges()); }); } @@ -256,12 +283,16 @@ public virtual void No_fixup_to_Deleted_entities( var root = LoadOptionalGraph(context); var existing = root.OptionalChildren.OrderBy(e => e.Id).First(); + Assert.False(context.ChangeTracker.HasChanges()); + existing.Parent = null; existing.ParentId = null; ((ICollection)root.OptionalChildren).Remove(existing); context.Entry(existing).State = EntityState.Deleted; + Assert.True(context.ChangeTracker.HasChanges()); + var queried = context.Set().ToList(); Assert.Null(existing.Parent); @@ -737,8 +768,12 @@ public virtual void Notification_entities_can_have_indexes() Assert.Equal(EntityState.Added, context.Entry(produce).State); + Assert.True(context.ChangeTracker.HasChanges()); + context.SaveChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(EntityState.Unchanged, context.Entry(produce).State); Assert.NotEqual(Guid.Empty, context.Entry(produce).Property(e => e.ProduceId).OriginalValue); Assert.Equal(77, context.Entry(produce).Property(e => e.BarCode).OriginalValue); @@ -748,8 +783,12 @@ public virtual void Notification_entities_can_have_indexes() Assert.NotEqual(Guid.Empty, context.Entry(produce).Property(e => e.ProduceId).OriginalValue); Assert.Equal(77, context.Entry(produce).Property(e => e.BarCode).OriginalValue); + Assert.True(context.ChangeTracker.HasChanges()); + context.SaveChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(EntityState.Detached, context.Entry(produce).State); }); } @@ -770,6 +809,8 @@ public virtual void Resetting_a_deleted_reference_fixes_up_again() context.Remove(bloog); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(2, bloog.Poosts.Count()); if (Fixture.ForceClientNoAction) @@ -815,8 +856,12 @@ public virtual void Resetting_a_deleted_reference_fixes_up_again() if (!Fixture.ForceClientNoAction) { + Assert.True(context.ChangeTracker.HasChanges()); + context.SaveChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(2, bloog.Poosts.Count()); Assert.Null(poost1.Bloog); Assert.Null(poost2.Bloog); @@ -875,6 +920,8 @@ public virtual void Detaching_principal_entity_will_remove_references_to_it() Assert.True(root.RequiredChildrenAk.All(e => e.Parent == root)); Assert.True(root.RequiredCompositeChildren.All(e => e.Parent == root)); + Assert.False(context.ChangeTracker.HasChanges()); + context.Entry(optionalSingle).State = EntityState.Detached; context.Entry(requiredSingle).State = EntityState.Detached; context.Entry(optionalSingleAk).State = EntityState.Detached; @@ -890,6 +937,8 @@ public virtual void Detaching_principal_entity_will_remove_references_to_it() context.Entry(requiredNonPkSingleMoreDerived).State = EntityState.Detached; context.Entry(requiredNonPkSingleAkMoreDerived).State = EntityState.Detached; + Assert.False(context.ChangeTracker.HasChanges()); + Assert.NotNull(optionalSingle.Root); Assert.NotNull(requiredSingle.Root); Assert.NotNull(optionalSingleAk.Root); @@ -975,6 +1024,8 @@ public virtual void Detaching_dependent_entity_will_not_remove_references_to_it( Assert.True(requiredChildrenAk.All(e => e.Parent == root)); Assert.True(requiredCompositeChildren.All(e => e.Parent == root)); + Assert.False(context.ChangeTracker.HasChanges()); + context.Entry(optionalSingle).State = EntityState.Detached; context.Entry(requiredSingle).State = EntityState.Detached; context.Entry(optionalSingleAk).State = EntityState.Detached; @@ -1001,6 +1052,8 @@ public virtual void Detaching_dependent_entity_will_not_remove_references_to_it( context.Entry(requiredCompositeChild).State = EntityState.Detached; + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Same(root, optionalSingle.Root); Assert.Same(root, requiredSingle.Root); Assert.Same(root, optionalSingleAk.Root); @@ -2232,6 +2285,8 @@ public virtual void Reparent_required_one_to_one( var root = LoadRequiredGraph(context); + Assert.False(context.ChangeTracker.HasChanges()); + context.Entry(newRoot).State = useExistingRoot ? EntityState.Unchanged : EntityState.Added; Assert.Equal( @@ -2547,6 +2602,8 @@ public virtual void Reparent_to_different_one_to_many( { var loadedRoot = LoadOptionalOneToManyGraph(context); + Assert.False(context.ChangeTracker.HasChanges()); + AssertKeys(root, loadedRoot); AssertNavigations(loadedRoot); @@ -3708,7 +3765,11 @@ public virtual void Save_changed_optional_one_to_one_with_alternate_key_in_store root2.OptionalSingleAkDerived = new1d; root2.OptionalSingleAkMoreDerived = new1dd; + Assert.True(context2.ChangeTracker.HasChanges()); + context2.SaveChanges(); + + Assert.False(context2.ChangeTracker.HasChanges()); } new1 = context.Set().Single(e => e.Id == new1.Id); @@ -3751,8 +3812,12 @@ public virtual void Save_changed_optional_one_to_one_with_alternate_key_in_store Assert.Equal(old1d.AlternateId, old2d.BackId); Assert.Equal(old1dd.AlternateId, old2dd.BackId); + Assert.True(context.ChangeTracker.HasChanges()); + context.SaveChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(root.AlternateId, new1.RootId); Assert.Equal(root.AlternateId, new1d.DerivedRootId); Assert.Equal(root.AlternateId, new1dd.MoreDerivedRootId); @@ -8673,8 +8738,12 @@ public virtual void Re_childing_parent_to_new_child_with_delete( var newChild = new ChildAsAParent(); parent.ChildAsAParent = newChild; + Assert.True(context.ChangeTracker.HasChanges()); + context.SaveChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + if (cascadeDeleteTiming == null) { context.ChangeTracker.CascadeChanges(); @@ -8693,6 +8762,7 @@ public virtual void Re_childing_parent_to_new_child_with_delete( context => { var parent = context.Set().Include(p => p.ChildAsAParent).Single(); + Assert.Equal(newId, parent.ChildAsAParentId); Assert.Equal(newId, parent.ChildAsAParent.Id); Assert.Null(context.Set().Find(oldId)); @@ -8717,8 +8787,12 @@ public virtual void Sometimes_not_calling_DetectChanges_when_required_does_not_t Assert.Null(dependent.BadCustomer); Assert.Empty(principal.BadOrders); + Assert.True(context.ChangeTracker.HasChanges()); + context.SaveChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Null(dependent.BadCustomerId); Assert.Null(dependent.BadCustomer); Assert.Empty(principal.BadOrders); @@ -8745,7 +8819,11 @@ public virtual void Can_add_valid_first_dependent_when_multiple_possible_princip context.Add(quizTask); + Assert.True(context.ChangeTracker.HasChanges()); + context.SaveChanges(); + + Assert.False(context.ChangeTracker.HasChanges()); }, context => { @@ -8770,7 +8848,11 @@ public virtual void Can_add_valid_second_dependent_when_multiple_possible_princi context.Add(hiddenAreaTask); + Assert.True(context.ChangeTracker.HasChanges()); + context.SaveChanges(); + + Assert.False(context.ChangeTracker.HasChanges()); }, context => { @@ -8802,7 +8884,11 @@ public virtual void Can_add_multiple_dependents_when_multiple_possible_principal context.Add(hiddenAreaTask); + Assert.True(context.ChangeTracker.HasChanges()); + context.SaveChanges(); + + Assert.False(context.ChangeTracker.HasChanges()); }, context => { diff --git a/test/EFCore.Specification.Tests/StoreGeneratedTestBase.cs b/test/EFCore.Specification.Tests/StoreGeneratedTestBase.cs index bd2266fe1c7..43407dd4d8a 100644 --- a/test/EFCore.Specification.Tests/StoreGeneratedTestBase.cs +++ b/test/EFCore.Specification.Tests/StoreGeneratedTestBase.cs @@ -517,6 +517,8 @@ public void Clearing_optional_FK_does_not_leave_temporary_value() var product = new OptionalProduct(); context.Add(product); + Assert.True(context.ChangeTracker.HasChanges()); + var productEntry = context.Entry(product); Assert.Equal(EntityState.Added, productEntry.State); @@ -530,6 +532,8 @@ public void Clearing_optional_FK_does_not_leave_temporary_value() context.SaveChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + productEntry = context.Entry(product); Assert.Equal(EntityState.Unchanged, productEntry.State); @@ -544,6 +548,8 @@ public void Clearing_optional_FK_does_not_leave_temporary_value() var category = new OptionalCategory(); product.Category = category; + Assert.True(context.ChangeTracker.HasChanges()); + productEntry = context.Entry(product); Assert.Equal(EntityState.Modified, productEntry.State); @@ -563,6 +569,8 @@ public void Clearing_optional_FK_does_not_leave_temporary_value() context.SaveChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + productEntry = context.Entry(product); Assert.Equal(EntityState.Unchanged, productEntry.State); @@ -598,8 +606,12 @@ public void Clearing_optional_FK_does_not_leave_temporary_value() Assert.Equal(1, category.Id); Assert.Equal(1, categoryEntry.Property(e => e.Id).CurrentValue); + Assert.True(context.ChangeTracker.HasChanges()); + context.SaveChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + productEntry = context.Entry(product); Assert.Equal(EntityState.Unchanged, productEntry.State); diff --git a/test/EFCore.Tests/ChangeTracking/ChangeTrackerTest.cs b/test/EFCore.Tests/ChangeTracking/ChangeTrackerTest.cs index 9be8eb6c891..dad60b7a60b 100644 --- a/test/EFCore.Tests/ChangeTracking/ChangeTrackerTest.cs +++ b/test/EFCore.Tests/ChangeTracking/ChangeTrackerTest.cs @@ -43,6 +43,8 @@ public async Task Keys_generated_on_behalf_of_a_principal_are_not_saved(bool asy context.Add(entity); } + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal( CoreStrings.UnknownKeyValue(nameof(Weak), nameof(Weak.HeroId)), Assert.Throws(() => context.SaveChanges()).Message); @@ -751,6 +753,8 @@ public void State_change_events_fire_from_Attach() context.Attach(new Cat(1)); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Single(tracked); Assert.Empty(changed); @@ -758,6 +762,8 @@ public void State_change_events_fire_from_Attach() context.Entry(new Cat(2)).State = EntityState.Unchanged; + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(2, tracked.Count); Assert.Empty(changed); @@ -777,6 +783,8 @@ public void State_change_events_fire_from_Add() context.Add(new Cat(1)); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Single(tracked); Assert.Empty(changed); @@ -784,6 +792,8 @@ public void State_change_events_fire_from_Add() context.Entry(new Cat(2)).State = EntityState.Added; + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(2, tracked.Count); Assert.Empty(changed); @@ -803,6 +813,8 @@ public void State_change_events_fire_from_Update() context.Update(new Cat(1)); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Single(tracked); Assert.Empty(changed); @@ -810,6 +822,8 @@ public void State_change_events_fire_from_Update() context.Entry(new Cat(2)).State = EntityState.Modified; + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(2, tracked.Count); Assert.Empty(changed); @@ -830,6 +844,8 @@ public void State_change_events_fire_for_tracked_state_changes() context.AddRange(new Cat(1), new Cat(2)); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(2, tracked.Count); Assert.Empty(changed); @@ -842,6 +858,8 @@ public void State_change_events_fire_for_tracked_state_changes() Assert.Equal(2, tracked.Count); Assert.Equal(2, changed.Count); + Assert.True(context.ChangeTracker.HasChanges()); + AssertChangedEvent(context, 1, EntityState.Added, EntityState.Unchanged, changed[0]); AssertChangedEvent(context, 2, EntityState.Added, EntityState.Modified, changed[1]); @@ -857,6 +875,8 @@ public void State_change_events_fire_for_tracked_state_changes() context.Remove(context.Cats.Find(1)); context.Entry(context.Cats.Find(2)).State = EntityState.Detached; + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(2, tracked.Count); Assert.Equal(6, changed.Count); @@ -915,8 +935,12 @@ public void State_change_events_fire_when_saving_changes() AssertTrackedEvent(context, 3, EntityState.Added, tracked[1], fromQuery: false); AssertChangedEvent(context, 1, EntityState.Unchanged, EntityState.Modified, changed[0]); + Assert.True(context.ChangeTracker.HasChanges()); + context.SaveChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(2, tracked.Count); Assert.Equal(3, changed.Count); @@ -940,6 +964,8 @@ public void State_change_events_fire_when_property_modified_flags_cause_state_ch var cat = context.Attach( new Cat(3) { Name = "Achilles" }).Entity; + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Single(tracked); Assert.Empty(changed); @@ -947,6 +973,8 @@ public void State_change_events_fire_when_property_modified_flags_cause_state_ch context.Entry(cat).Property(e => e.Name).IsModified = true; + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Single(tracked); Assert.Single(changed); @@ -954,6 +982,8 @@ public void State_change_events_fire_when_property_modified_flags_cause_state_ch context.Entry(cat).Property(e => e.Name).IsModified = false; + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Single(tracked); Assert.Equal(2, changed.Count); @@ -2253,11 +2283,6 @@ public EarlyLearningCenter() _serviceProvider = InMemoryTestHelpers.Instance.CreateServiceProvider(); } - public EarlyLearningCenter(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - protected internal override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity().OwnsOne( @@ -2303,23 +2328,5 @@ protected internal override void OnConfiguring(DbContextOptionsBuilder optionsBu .UseInternalServiceProvider(_serviceProvider) .UseInMemoryDatabase(nameof(EarlyLearningCenter)); } - - public class KeyValueEntityTracker - { - private readonly bool _updateExistingEntities; - - public KeyValueEntityTracker(bool updateExistingEntities) - { - _updateExistingEntities = updateExistingEntities; - } - - public virtual void TrackEntity(EntityEntryGraphNode node) - => node.Entry.GetInfrastructure().SetEntityState(DetermineState(node.Entry), acceptChanges: true); - - public virtual EntityState DetermineState(EntityEntry entry) - => entry.IsKeySet - ? (_updateExistingEntities ? EntityState.Modified : EntityState.Unchanged) - : EntityState.Added; - } } } diff --git a/test/EFCore.Tests/ChangeTracking/Internal/OwnedFixupTest.cs b/test/EFCore.Tests/ChangeTracking/Internal/OwnedFixupTest.cs index 5ec48473b9a..d000a80bd87 100644 --- a/test/EFCore.Tests/ChangeTracking/Internal/OwnedFixupTest.cs +++ b/test/EFCore.Tests/ChangeTracking/Internal/OwnedFixupTest.cs @@ -60,6 +60,8 @@ public void Detaching_owner_does_not_delete_owned_entities(bool delayCascade) context.Attach(thing); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); Assert.Equal(EntityState.Unchanged, context.Entry(thing).State); Assert.Equal(EntityState.Unchanged, context.Entry(thing.OwnedByThings[0]).State); @@ -72,6 +74,8 @@ public void Detaching_owner_does_not_delete_owned_entities(bool delayCascade) context.Entry(thing).State = EntityState.Detached; + Assert.False(context.ChangeTracker.HasChanges()); + if (delayCascade) { Assert.Equal(2, context.ChangeTracker.Entries().Count()); @@ -96,11 +100,15 @@ public void Can_detach_Added_owner_referencing_detached_weak_owned_entity() context.Entry(owner).State = EntityState.Added; + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(EntityState.Added, context.Entry(owner).State); Assert.Equal(EntityState.Detached, context.Entry(owner).Reference(e => e.Child1).TargetEntry.State); context.Entry(owner).State = EntityState.Detached; + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(EntityState.Detached, context.Entry(owner).State); Assert.Equal(EntityState.Detached, context.Entry(owner).Reference(e => e.Child1).TargetEntry.State); } @@ -209,6 +217,11 @@ public void Add_principal_with_dependent_unidirectional_nav(EntityState entitySt } } + Assert.Equal( + entityState != EntityState.Unchanged + || useTrackGraph == null, + context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); AssertFixup( @@ -281,6 +294,11 @@ public void Add_principal_with_dependent_both_navs(EntityState entityState, bool } } + Assert.Equal( + entityState != EntityState.Unchanged + || useTrackGraph == null, + context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); AssertFixup( @@ -351,6 +369,11 @@ public void Add_principal_with_dependent_principal_nav(EntityState entityState, } } + Assert.Equal( + entityState != EntityState.Unchanged + || useTrackGraph == null, + context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); AssertFixup( @@ -483,6 +506,11 @@ public void Add_principal_with_dependent_unidirectional_nav_collection( } } + Assert.Equal( + entityState != EntityState.Unchanged + || useTrackGraph == null, + context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); AssertFixup( @@ -617,6 +645,11 @@ public void Add_principal_with_dependent_both_navs_collection( } } + Assert.Equal( + entityState != EntityState.Unchanged + || useTrackGraph == null, + context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); AssertFixup( @@ -750,6 +783,11 @@ public void Add_principal_with_dependent_principal_nav_collection( } } + Assert.Equal( + entityState != EntityState.Unchanged + || useTrackGraph == null, + context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); AssertFixup( @@ -824,6 +862,8 @@ public void Instance_changed_unidirectional(EntityState entityState) context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); Assert.Null(principal.Child1); Assert.Same(dependent2, principal.Child2); @@ -842,15 +882,20 @@ public void Instance_changed_unidirectional(EntityState entityState) context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Null(principal.Child1); Assert.Same(dependent2, principal.Child2); Assert.Same(subDependent2, dependent2.SubChild); + Assert.False(context.ChangeTracker.HasChanges()); } [ConditionalTheory] @@ -880,6 +925,8 @@ public void Instance_changed_bidirectional(EntityState entityState) context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); Assert.Null(principal.Child2); Assert.Same(principal, dependent2.Parent); @@ -900,10 +947,14 @@ public void Instance_changed_bidirectional(EntityState entityState) context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Null(principal.Child2); @@ -974,6 +1025,8 @@ public void Instance_changed_unidirectional_collection(EntityState entityState, context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); Assert.Null(principal.ChildCollection1); Assert.Contains(principal.ChildCollection2, e => ReferenceEquals(e, dependent2)); @@ -990,10 +1043,14 @@ public void Instance_changed_unidirectional_collection(EntityState entityState, context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Null(principal.ChildCollection1); @@ -1064,6 +1121,8 @@ public void Instance_changed_bidirectional_collection(EntityState entityState, C context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); Assert.Null(principal.ChildCollection2); Assert.Same(principal, dependent2.Parent); @@ -1082,10 +1141,14 @@ public void Instance_changed_bidirectional_collection(EntityState entityState, C context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Null(principal.ChildCollection2); @@ -1129,6 +1192,8 @@ public void Identity_changed_unidirectional(EntityState entityState) context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(entityState == EntityState.Added ? 3 : 5, context.ChangeTracker.Entries().Count()); Assert.Null(principal.Child1); Assert.Same(dependent, principal.Child2); @@ -1147,10 +1212,14 @@ public void Identity_changed_unidirectional(EntityState entityState) context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(entityState == EntityState.Added ? 3 : 5, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Null(principal.Child1); @@ -1182,6 +1251,8 @@ public void Identity_changed_bidirectional(EntityState entityState) context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(entityState == EntityState.Added ? 3 : 5, context.ChangeTracker.Entries().Count()); Assert.Null(principal.Child2); Assert.Same(principal, dependent.Parent); @@ -1202,10 +1273,14 @@ public void Identity_changed_bidirectional(EntityState entityState) context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(entityState == EntityState.Added ? 3 : 5, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Null(principal.Child2); @@ -1264,6 +1339,8 @@ public void Identity_changed_unidirectional_collection(EntityState entityState, context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(entityState == EntityState.Added ? 3 : 5, context.ChangeTracker.Entries().Count()); Assert.Null(principal.ChildCollection1); Assert.Contains(principal.ChildCollection2, e => ReferenceEquals(e, dependent)); @@ -1282,10 +1359,14 @@ public void Identity_changed_unidirectional_collection(EntityState entityState, context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(entityState == EntityState.Added ? 3 : 5, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Null(principal.ChildCollection1); @@ -1343,6 +1424,8 @@ public void Identity_changed_bidirectional_collection(EntityState entityState, C context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(entityState == EntityState.Added ? 3 : 5, context.ChangeTracker.Entries().Count()); Assert.Null(principal.ChildCollection2); Assert.Same(principal, dependent.Parent); @@ -1363,10 +1446,14 @@ public void Identity_changed_bidirectional_collection(EntityState entityState, C context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(entityState == EntityState.Added ? 3 : 5, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Null(principal.Child2); @@ -1398,11 +1485,15 @@ public void Identity_swapped_unidirectional(EntityState entityState) context.ChangeTracker.TrackGraph(principal, e => e.Entry.State = entityState); + Assert.Equal(entityState != EntityState.Unchanged, context.ChangeTracker.HasChanges()); + principal.Child2 = dependent1; principal.Child1 = dependent2; context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(5, context.ChangeTracker.Entries().Count()); Assert.Same(dependent1, principal.Child2); Assert.Same(dependent2, principal.Child1); @@ -1438,10 +1529,14 @@ public void Identity_swapped_unidirectional(EntityState entityState) context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(5, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(5, context.ChangeTracker.Entries().Count()); Assert.Null(dependent1Entry.GetInfrastructure().SharedIdentityEntry); Assert.Null(dependent2Entry.GetInfrastructure().SharedIdentityEntry); @@ -1475,11 +1570,15 @@ public void Identity_swapped_bidirectional(EntityState entityState) context.ChangeTracker.TrackGraph(principal, e => e.Entry.State = entityState); + Assert.Equal(entityState != EntityState.Unchanged, context.ChangeTracker.HasChanges()); + principal.Child2 = dependent1; principal.Child1 = dependent2; context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(5, context.ChangeTracker.Entries().Count()); Assert.Same(principal, dependent1.Parent); Assert.Same(dependent1, principal.Child2); @@ -1519,10 +1618,14 @@ public void Identity_swapped_bidirectional(EntityState entityState) context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(5, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(5, context.ChangeTracker.Entries().Count()); Assert.Null(dependent1Entry.GetInfrastructure().SharedIdentityEntry); Assert.Null(dependent2Entry.GetInfrastructure().SharedIdentityEntry); @@ -1583,6 +1686,8 @@ public void Identity_swapped_unidirectional_collection(EntityState entityState, break; } + Assert.Equal(entityState != EntityState.Unchanged, context.ChangeTracker.HasChanges()); + var dependentEntry1 = context.Entry(dependent1); var dependentEntry2 = context.Entry(dependent2); var subDependentEntry1 = context.Entry(subDependent1); @@ -1610,6 +1715,8 @@ public void Identity_swapped_unidirectional_collection(EntityState entityState, context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(5, context.ChangeTracker.Entries().Count()); Assert.Contains(principal.ChildCollection2, e => ReferenceEquals(e, dependent1)); Assert.Contains(principal.ChildCollection1, e => ReferenceEquals(e, dependent2)); @@ -1641,10 +1748,14 @@ public void Identity_swapped_unidirectional_collection(EntityState entityState, context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(5, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(5, context.ChangeTracker.Entries().Count()); Assert.Null(newDependentEntry2.GetInfrastructure().SharedIdentityEntry); Assert.Null(newDependentEntry1.GetInfrastructure().SharedIdentityEntry); @@ -1704,6 +1815,8 @@ public void Identity_swapped_bidirectional_collection(EntityState entityState, C break; } + Assert.Equal(entityState != EntityState.Unchanged, context.ChangeTracker.HasChanges()); + var dependentEntry1 = context.Entry(dependent1); var dependentEntry2 = context.Entry(dependent2); var subDependentEntry1 = context.Entry(subDependent1); @@ -1731,6 +1844,8 @@ public void Identity_swapped_bidirectional_collection(EntityState entityState, C context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(5, context.ChangeTracker.Entries().Count()); Assert.Same(principal, dependent1.Parent); Assert.Contains(principal.ChildCollection2, e => ReferenceEquals(e, dependent1)); @@ -1766,10 +1881,14 @@ public void Identity_swapped_bidirectional_collection(EntityState entityState, C context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(5, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(5, context.ChangeTracker.Entries().Count()); Assert.Null(newDependentEntry2.GetInfrastructure().SharedIdentityEntry); Assert.Null(newDependentEntry1.GetInfrastructure().SharedIdentityEntry); @@ -1800,6 +1919,8 @@ public void Parent_changed_unidirectional(EntityState entityState) context.ChangeTracker.TrackGraph(principal1, e => e.Entry.State = entityState); context.ChangeTracker.TrackGraph(principal2, e => e.Entry.State = entityState); + Assert.Equal(entityState != EntityState.Unchanged, context.ChangeTracker.HasChanges()); + var dependentEntry1 = context.Entry(principal1).Reference(p => p.Child1).TargetEntry; principal2.Child1 = dependent; @@ -1813,6 +1934,8 @@ public void Parent_changed_unidirectional(EntityState entityState) } else { + Assert.True(context.ChangeTracker.HasChanges()); + context.ChangeTracker.DetectChanges(); Assert.Equal(4, context.ChangeTracker.Entries().Count()); @@ -1837,10 +1960,14 @@ public void Parent_changed_unidirectional(EntityState entityState) context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(4, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(4, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Null(principal1.Child1); @@ -1871,6 +1998,8 @@ public void Parent_changed_bidirectional(EntityState entityState) context.ChangeTracker.TrackGraph(principal1, e => e.Entry.State = entityState); context.ChangeTracker.TrackGraph(principal2, e => e.Entry.State = entityState); + Assert.Equal(entityState != EntityState.Unchanged, context.ChangeTracker.HasChanges()); + var dependentEntry1 = context.Entry(principal1).Reference(p => p.Child1).TargetEntry; principal2.Child1 = dependent; @@ -1886,6 +2015,8 @@ public void Parent_changed_bidirectional(EntityState entityState) { context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(4, context.ChangeTracker.Entries().Count()); Assert.Null(principal1.Child1); Assert.Null(principal1.Child2); @@ -1909,10 +2040,14 @@ public void Parent_changed_bidirectional(EntityState entityState) context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(4, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(4, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Null(principal1.Child1); @@ -1972,6 +2107,8 @@ public void Parent_changed_unidirectional_collection(EntityState entityState, Co break; } + Assert.Equal(entityState != EntityState.Unchanged, context.ChangeTracker.HasChanges()); + var dependentEntry1 = context.Entry(dependent); var subDependentEntry1 = context.Entry(subDependent); @@ -1988,6 +2125,8 @@ public void Parent_changed_unidirectional_collection(EntityState entityState, Co { context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(4, context.ChangeTracker.Entries().Count()); Assert.Null(principal1.ChildCollection1); Assert.Null(principal1.ChildCollection2); @@ -2012,10 +2151,14 @@ public void Parent_changed_unidirectional_collection(EntityState entityState, Co context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(4, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(4, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Null(principal1.ChildCollection1); @@ -2074,6 +2217,8 @@ public void Parent_changed_bidirectional_collection(EntityState entityState, Col break; } + Assert.Equal(entityState != EntityState.Unchanged, context.ChangeTracker.HasChanges()); + var dependentEntry1 = context.Entry(dependent); var subDependentEntry1 = context.Entry(subDependent); @@ -2090,6 +2235,8 @@ public void Parent_changed_bidirectional_collection(EntityState entityState, Col { context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(4, context.ChangeTracker.Entries().Count()); Assert.Empty(principal1.ChildCollection1); Assert.Null(principal1.ChildCollection2); @@ -2116,10 +2263,14 @@ public void Parent_changed_bidirectional_collection(EntityState entityState, Col context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(4, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(4, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Empty(principal1.ChildCollection1); @@ -2157,6 +2308,8 @@ public void Parent_swapped_unidirectional(EntityState entityState) context.ChangeTracker.TrackGraph(principal1, e => e.Entry.State = entityState); context.ChangeTracker.TrackGraph(principal2, e => e.Entry.State = entityState); + Assert.Equal(entityState != EntityState.Unchanged, context.ChangeTracker.HasChanges()); + principal1.Child1 = dependent2; principal2.Child1 = dependent1; @@ -2172,6 +2325,8 @@ public void Parent_swapped_unidirectional(EntityState entityState) { context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); Assert.Same(dependent2, principal1.Child1); Assert.Null(principal1.Child2); @@ -2204,10 +2359,14 @@ public void Parent_swapped_unidirectional(EntityState entityState) context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Same(dependent2, principal1.Child1); @@ -2245,6 +2404,8 @@ public void Parent_swapped_bidirectional(EntityState entityState) context.ChangeTracker.TrackGraph(principal1, e => e.Entry.State = entityState); context.ChangeTracker.TrackGraph(principal2, e => e.Entry.State = entityState); + Assert.Equal(entityState != EntityState.Unchanged, context.ChangeTracker.HasChanges()); + principal1.Child1 = dependent2; principal2.Child1 = dependent1; @@ -2260,6 +2421,8 @@ public void Parent_swapped_bidirectional(EntityState entityState) { context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); Assert.Same(dependent2, principal1.Child1); Assert.Null(principal1.Child2); @@ -2296,10 +2459,14 @@ public void Parent_swapped_bidirectional(EntityState entityState) context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Same(dependent2, principal1.Child1); @@ -2367,6 +2534,8 @@ public void Parent_swapped_unidirectional_collection(EntityState entityState, Co break; } + Assert.Equal(entityState != EntityState.Unchanged, context.ChangeTracker.HasChanges()); + var dependentEntry1 = context.Entry(dependent1); var subDependentEntry1 = context.Entry(subDependent1); @@ -2386,6 +2555,8 @@ public void Parent_swapped_unidirectional_collection(EntityState entityState, Co { context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); Assert.Contains(principal1.ChildCollection1, e => ReferenceEquals(e, dependent2)); Assert.Null(principal1.Child1); @@ -2422,10 +2593,14 @@ public void Parent_swapped_unidirectional_collection(EntityState entityState, Co context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Contains(principal1.ChildCollection1, e => ReferenceEquals(e, dependent2)); @@ -2491,6 +2666,8 @@ public void Parent_swapped_bidirectional_collection(EntityState entityState, Col break; } + Assert.Equal(entityState != EntityState.Unchanged, context.ChangeTracker.HasChanges()); + var dependentEntry1 = context.Entry(dependent1); var subDependentEntry1 = context.Entry(subDependent1); @@ -2510,6 +2687,8 @@ public void Parent_swapped_bidirectional_collection(EntityState entityState, Col { context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); Assert.Contains(principal1.ChildCollection1, e => ReferenceEquals(e, dependent2)); Assert.Null(principal1.Child1); @@ -2550,10 +2729,14 @@ public void Parent_swapped_bidirectional_collection(EntityState entityState, Col context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Contains(principal1.ChildCollection1, e => ReferenceEquals(e, dependent2)); @@ -2587,6 +2770,8 @@ public void Parent_and_identity_changed_unidirectional(EntityState entityState) context.ChangeTracker.TrackGraph(principal1, e => e.Entry.State = entityState); context.ChangeTracker.TrackGraph(principal2, e => e.Entry.State = entityState); + Assert.Equal(entityState != EntityState.Unchanged, context.ChangeTracker.HasChanges()); + var dependentEntry1 = context.Entry(principal1).Reference(p => p.Child2).TargetEntry; principal2.Child1 = dependent; @@ -2594,6 +2779,8 @@ public void Parent_and_identity_changed_unidirectional(EntityState entityState) context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(entityState == EntityState.Added ? 4 : 6, context.ChangeTracker.Entries().Count()); Assert.Null(principal1.Child1); Assert.Null(principal1.Child2); @@ -2615,10 +2802,14 @@ public void Parent_and_identity_changed_unidirectional(EntityState entityState) context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(entityState == EntityState.Added ? 4 : 6, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(4, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Null(principal1.Child1); @@ -2648,6 +2839,8 @@ public void Parent_and_identity_changed_bidirectional(EntityState entityState) context.ChangeTracker.TrackGraph(principal1, e => e.Entry.State = entityState); context.ChangeTracker.TrackGraph(principal2, e => e.Entry.State = entityState); + Assert.Equal(entityState != EntityState.Unchanged, context.ChangeTracker.HasChanges()); + var dependentEntry1 = context.Entry(principal1).Reference(p => p.Child2).TargetEntry; principal2.Child1 = dependent; @@ -2655,6 +2848,8 @@ public void Parent_and_identity_changed_bidirectional(EntityState entityState) context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(entityState == EntityState.Added ? 4 : 6, context.ChangeTracker.Entries().Count()); Assert.Null(principal1.Child1); Assert.Null(principal1.Child2); @@ -2678,10 +2873,14 @@ public void Parent_and_identity_changed_bidirectional(EntityState entityState) context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(entityState == EntityState.Added ? 4 : 6, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(4, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Null(principal1.Child1); @@ -2740,6 +2939,8 @@ public void Parent_and_identity_changed_unidirectional_collection(EntityState en break; } + Assert.Equal(entityState != EntityState.Unchanged, context.ChangeTracker.HasChanges()); + var dependentEntry1 = context.Entry(principal1).Collection(p => p.ChildCollection2).FindEntry(dependent); principal2.ChildCollection1 = principal1.ChildCollection2; @@ -2747,6 +2948,8 @@ public void Parent_and_identity_changed_unidirectional_collection(EntityState en context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(entityState == EntityState.Added ? 4 : 6, context.ChangeTracker.Entries().Count()); Assert.Null(principal1.ChildCollection1); Assert.Null(principal1.ChildCollection2); @@ -2768,10 +2971,14 @@ public void Parent_and_identity_changed_unidirectional_collection(EntityState en context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(entityState == EntityState.Added ? 4 : 6, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(4, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Null(principal1.ChildCollection1); @@ -2830,6 +3037,8 @@ public void Parent_and_identity_changed_bidirectional_collection(EntityState ent break; } + Assert.Equal(entityState != EntityState.Unchanged, context.ChangeTracker.HasChanges()); + var dependentEntry1 = context.Entry(principal1).Collection(p => p.ChildCollection2).FindEntry(dependent); principal2.ChildCollection1 = principal1.ChildCollection2; @@ -2837,6 +3046,8 @@ public void Parent_and_identity_changed_bidirectional_collection(EntityState ent context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(entityState == EntityState.Added ? 4 : 6, context.ChangeTracker.Entries().Count()); Assert.Empty(principal1.ChildCollection1); Assert.Null(principal1.ChildCollection2); @@ -2860,10 +3071,14 @@ public void Parent_and_identity_changed_bidirectional_collection(EntityState ent context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(entityState == EntityState.Added ? 4 : 6, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(4, context.ChangeTracker.Entries().Count()); Assert.True(context.ChangeTracker.Entries().All(e => e.State == EntityState.Unchanged)); Assert.Empty(principal1.ChildCollection1); @@ -2900,11 +3115,15 @@ public void Parent_and_identity_swapped_unidirectional(EntityState entityState) context.ChangeTracker.TrackGraph(principal1, e => e.Entry.State = entityState); context.ChangeTracker.TrackGraph(principal2, e => e.Entry.State = entityState); + Assert.Equal(entityState != EntityState.Unchanged, context.ChangeTracker.HasChanges()); + principal2.Child1 = dependent1; principal1.Child2 = dependent2; context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); Assert.Null(principal1.Child1); Assert.Same(dependent2, principal1.Child2); @@ -2943,10 +3162,14 @@ public void Parent_and_identity_swapped_unidirectional(EntityState entityState) context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); Assert.Null(dependent1Entry.GetInfrastructure().SharedIdentityEntry); Assert.Null(dependent2Entry.GetInfrastructure().SharedIdentityEntry); @@ -2985,11 +3208,15 @@ public void Parent_and_identity_swapped_bidirectional(EntityState entityState) context.ChangeTracker.TrackGraph(principal1, e => e.Entry.State = entityState); context.ChangeTracker.TrackGraph(principal2, e => e.Entry.State = entityState); + Assert.Equal(entityState != EntityState.Unchanged, context.ChangeTracker.HasChanges()); + principal2.Child1 = dependent1; principal1.Child2 = dependent2; context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); Assert.Null(principal1.Child1); Assert.Same(dependent2, principal1.Child2); @@ -3032,10 +3259,14 @@ public void Parent_and_identity_swapped_bidirectional(EntityState entityState) context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); Assert.Null(dependent1Entry.GetInfrastructure().SharedIdentityEntry); Assert.Null(dependent2Entry.GetInfrastructure().SharedIdentityEntry); @@ -3104,6 +3335,8 @@ public void Parent_and_identity_swapped_unidirectional_collection(EntityState en break; } + Assert.Equal(entityState != EntityState.Unchanged, context.ChangeTracker.HasChanges()); + var dependentEntry1 = context.Entry(dependent1); var dependentEntry2 = context.Entry(dependent2); var subDependentEntry1 = context.Entry(subDependent1); @@ -3129,6 +3362,8 @@ public void Parent_and_identity_swapped_unidirectional_collection(EntityState en .FindEntry(subDependent2); newSubDependentEntry2.Property("Id").CurrentValue = subDependentEntry2.Property("Id").CurrentValue; + Assert.Equal(entityState != EntityState.Unchanged, context.ChangeTracker.HasChanges()); + context.ChangeTracker.DetectChanges(); Assert.Equal(6, context.ChangeTracker.Entries().Count()); @@ -3165,10 +3400,14 @@ public void Parent_and_identity_swapped_unidirectional_collection(EntityState en context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); Assert.Null(newDependentEntry2.GetInfrastructure().SharedIdentityEntry); Assert.Null(newDependentEntry1.GetInfrastructure().SharedIdentityEntry); @@ -3209,7 +3448,6 @@ public void Parent_and_identity_swapped_bidirectional_collection(EntityState ent var dependent1 = new Child { Name = "1" }; principal1.ChildCollection2 = CreateChildCollection(collectionType, dependent1); - ; var subDependent1 = new SubChild { Name = "1S" }; dependent1.SubChildCollection = CreateChildCollection(collectionType, subDependent1); @@ -3236,6 +3474,8 @@ public void Parent_and_identity_swapped_bidirectional_collection(EntityState ent break; } + Assert.Equal(entityState != EntityState.Unchanged, context.ChangeTracker.HasChanges()); + var dependentEntry1 = context.Entry(dependent1); var dependentEntry2 = context.Entry(dependent2); var subDependentEntry1 = context.Entry(subDependent1); @@ -3263,6 +3503,8 @@ public void Parent_and_identity_swapped_bidirectional_collection(EntityState ent context.ChangeTracker.DetectChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); Assert.Empty(principal1.ChildCollection1); Assert.Contains(principal1.ChildCollection2, e => ReferenceEquals(e, dependent2)); @@ -3301,10 +3543,14 @@ public void Parent_and_identity_swapped_bidirectional_collection(EntityState ent context.ChangeTracker.CascadeChanges(); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); context.ChangeTracker.AcceptAllChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(6, context.ChangeTracker.Entries().Count()); Assert.Null(newDependentEntry2.GetInfrastructure().SharedIdentityEntry); Assert.Null(newDependentEntry1.GetInfrastructure().SharedIdentityEntry); @@ -3330,8 +3576,13 @@ public void Fixup_works_when_changing_state_from_Detached_to_Modified(bool detac var product = new Product { Name = "Product1", Details = details }; context.Add(product); + + Assert.True(context.ChangeTracker.HasChanges()); + context.SaveChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(2, context.ChangeTracker.Entries().Count()); Assert.Equal(EntityState.Unchanged, context.Entry(product).State); Assert.Equal(EntityState.Unchanged, context.Entry(details).State); @@ -3342,6 +3593,8 @@ public void Fixup_works_when_changing_state_from_Detached_to_Modified(bool detac context.Entry(details).State = EntityState.Detached; } + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Empty(context.ChangeTracker.Entries()); Assert.Equal(EntityState.Detached, context.Entry(details).State); @@ -3356,6 +3609,8 @@ public void Fixup_works_when_changing_state_from_Detached_to_Modified(bool detac context.Update(newProduct); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(2, context.ChangeTracker.Entries().Count()); Assert.Equal(EntityState.Modified, context.Entry(newProduct).State); @@ -3368,6 +3623,8 @@ public void Fixup_works_when_changing_state_from_Detached_to_Modified(bool detac context.SaveChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(2, context.ChangeTracker.Entries().Count()); Assert.Equal(EntityState.Unchanged, context.Entry(newProduct).State); Assert.Equal(EntityState.Unchanged, context.Entry(newDetails).State); @@ -3414,6 +3671,8 @@ public void Can_save_multiple_deep_owned_entities() context.Add(distributor); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); Assert.Equal(EntityState.Added, context.Entry(distributor).State); Assert.Equal(EntityState.Added, context.Entry(address1).State); @@ -3425,6 +3684,8 @@ public void Can_save_multiple_deep_owned_entities() context.SaveChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(3, context.ChangeTracker.Entries().Count()); Assert.Equal(EntityState.Unchanged, context.Entry(distributor).State); Assert.Equal(EntityState.Unchanged, context.Entry(address1).State); @@ -3488,8 +3749,13 @@ public void Can_replace_owned_entity_after_deleting() using (var context = new BooksContext(nameof(BooksContext))) { context.Books.Add(book); + + Assert.True(context.ChangeTracker.HasChanges()); + context.SaveChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Same(info, book.EnglishInfo); Assert.Equal("MyBook", book.EnglishInfo.Title); } @@ -3498,6 +3764,8 @@ public void Can_replace_owned_entity_after_deleting() { context.Attach(book); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Same(info, book.EnglishInfo); Assert.Equal("MyBook", book.EnglishInfo.Title); @@ -3515,8 +3783,13 @@ public void Can_replace_owned_entity_after_deleting() }; context.Remove(book); + + Assert.True(context.ChangeTracker.HasChanges()); + context.Add(newBook); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(4, context.ChangeTracker.Entries().Count()); Assert.Equal(EntityState.Deleted, context.Entry(book).State); Assert.Equal(EntityState.Deleted, context.Entry(info).State); @@ -3530,6 +3803,90 @@ public void Can_replace_owned_entity_after_deleting() context.SaveChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + + Assert.Equal(2, context.ChangeTracker.Entries().Count()); + Assert.Equal(EntityState.Unchanged, context.Entry(newBook).State); + Assert.Equal(EntityState.Unchanged, context.Entry(newInfo).State); + + Assert.Same(info, book.EnglishInfo); + Assert.Equal("MyBook", book.EnglishInfo.Title); + Assert.Same(newInfo, newBook.EnglishInfo); + Assert.Equal("MyBook Rev 2", newBook.EnglishInfo.Title); + } + } + + [ConditionalFact] + public void Can_replace_owned_entity_with_unchanged_entity_after_deleting() + { + const long MyBookId = 1534; + + var info = new Info { Title = "MyBook" }; + + var book = new Book + { + BookId = MyBookId, + Pages = 99, + EnglishInfo = info + }; + + using (var context = new BooksContext(nameof(BooksContext))) + { + context.Books.Add(book); + + Assert.True(context.ChangeTracker.HasChanges()); + + context.SaveChanges(); + + Assert.False(context.ChangeTracker.HasChanges()); + + Assert.Same(info, book.EnglishInfo); + Assert.Equal("MyBook", book.EnglishInfo.Title); + } + + using (var context = new BooksContext(nameof(BooksContext))) + { + context.Attach(book); + + Assert.False(context.ChangeTracker.HasChanges()); + + Assert.Same(info, book.EnglishInfo); + Assert.Equal("MyBook", book.EnglishInfo.Title); + + Assert.Equal(2, context.ChangeTracker.Entries().Count()); + Assert.Equal(EntityState.Unchanged, context.Entry(book).State); + Assert.Equal(EntityState.Unchanged, context.Entry(info).State); + + var newInfo = new Info { Title = "MyBook Rev 2" }; + + var newBook = new Book + { + BookId = MyBookId, + Pages = 100, + EnglishInfo = newInfo + }; + + context.Remove(book); + + Assert.True(context.ChangeTracker.HasChanges()); + + context.Attach(newBook); + + Assert.False(context.ChangeTracker.HasChanges()); + + Assert.Equal(2, context.ChangeTracker.Entries().Count()); + Assert.Equal(EntityState.Unchanged, context.Entry(newBook).State); + Assert.Equal(EntityState.Unchanged, context.Entry(newInfo).State); + + Assert.Same(info, book.EnglishInfo); + Assert.Equal("MyBook", book.EnglishInfo.Title); + Assert.Same(newInfo, newBook.EnglishInfo); + Assert.Equal("MyBook Rev 2", newBook.EnglishInfo.Title); + + context.SaveChanges(); + + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(2, context.ChangeTracker.Entries().Count()); Assert.Equal(EntityState.Unchanged, context.Entry(newBook).State); Assert.Equal(EntityState.Unchanged, context.Entry(newInfo).State); @@ -3610,12 +3967,16 @@ public void Entities_with_owned_custom_enum_pattern_are_tracked_correctly_if_not context.TestOrders.Add(order); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(2, order.TestOrderItems.Count); Assert.Equal("EUR", order.TestOrderItems.Single(e => e.ProductName == "Test Product 1").Price.Currency.Code); Assert.Equal("USD", order.TestOrderItems.Single(e => e.ProductName == "Test Product 3").Price.Currency.Code); context.SaveChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(2, order.TestOrderItems.Count); Assert.Equal("EUR", order.TestOrderItems.Single(e => e.ProductName == "Test Product 1").Price.Currency.Code); Assert.Equal("USD", order.TestOrderItems.Single(e => e.ProductName == "Test Product 3").Price.Currency.Code); @@ -3738,6 +4099,8 @@ public void Entities_with_owned_custom_enum_pattern_using_ValueConverter_are_tra context.Add(order); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(4, order.TestOrderItems.Count); Assert.Equal("EUR", order.TestOrderItems.Single(e => e.ProductName == "Test Product 1").Price.Currency.Code); Assert.Equal("EUR", order.TestOrderItems.Single(e => e.ProductName == "Test Product 2").Price.Currency.Code); @@ -3746,6 +4109,8 @@ public void Entities_with_owned_custom_enum_pattern_using_ValueConverter_are_tra context.SaveChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(4, order.TestOrderItems.Count); Assert.Equal("EUR", order.TestOrderItems.Single(e => e.ProductName == "Test Product 1").Price.Currency.Code); Assert.Equal("EUR", order.TestOrderItems.Single(e => e.ProductName == "Test Product 2").Price.Currency.Code); @@ -3800,8 +4165,13 @@ EntityState GetEntryState(EquatableEntitiesContext context, string role new[] { new Role { Value = "Pascal" }, new Role { Value = "Smalltalk" }, new Role { Value = "COBOL" } }); context.Add(user); + + Assert.True(context.ChangeTracker.HasChanges()); + context.SaveChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(4, context.ChangeTracker.Entries().Count()); Assert.Equal(EntityState.Unchanged, GetEntryState(context)); Assert.Equal(EntityState.Unchanged, GetEntryState(context, "Pascal")); @@ -3827,8 +4197,12 @@ EntityState GetEntryState(EquatableEntitiesContext context, string role Assert.Equal(1, user.Roles.Count); Assert.Equal("BASIC", user.Roles.Select(e => e.Value).Single()); + Assert.True(context.ChangeTracker.HasChanges()); + context.SaveChanges(); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(2, context.ChangeTracker.Entries().Count()); Assert.Equal(EntityState.Unchanged, GetEntryState(context)); Assert.Equal(EntityState.Unchanged, GetEntryState(context, "BASIC")); diff --git a/test/EFCore.Tests/ChangeTracking/TrackGraphTestBase.cs b/test/EFCore.Tests/ChangeTracking/TrackGraphTestBase.cs index f9bbc637a0a..fe779134fad 100644 --- a/test/EFCore.Tests/ChangeTracking/TrackGraphTestBase.cs +++ b/test/EFCore.Tests/ChangeTracking/TrackGraphTestBase.cs @@ -123,6 +123,8 @@ public void Can_attach_nullable_PK_parent_with_child_collection(bool useAttach, category, node => node.Entry.State = node.Entry.IsKeySet ? EntityState.Unchanged : EntityState.Added)); } + Assert.Equal(!setKeys, context.ChangeTracker.HasChanges()); + Assert.Equal(4, context.ChangeTracker.Entries().Count()); var categoryEntry = context.Entry(category); @@ -177,6 +179,8 @@ public void Can_attach_nullable_PK_parent_with_one_to_one_children(bool useAttac category, node => node.Entry.State = node.Entry.IsKeySet ? EntityState.Unchanged : EntityState.Added)); } + Assert.Equal(!setKeys, context.ChangeTracker.HasChanges()); + Assert.Equal(2, context.ChangeTracker.Entries().Count()); var expectedState = setKeys ? EntityState.Unchanged : EntityState.Added; @@ -246,6 +250,10 @@ public void Can_attach_parent_with_owned_dependent(bool useAttach, bool setPrinc var expectedPrincipalState = setPrincipalKey ? EntityState.Unchanged : EntityState.Added; var expectedDependentState = setPrincipalKey || (setDependentKey && useAttach) ? EntityState.Unchanged : EntityState.Added; + Assert.Equal( + expectedPrincipalState == EntityState.Added || expectedDependentState == EntityState.Added, + context.ChangeTracker.HasChanges()); + Assert.Equal(expectedPrincipalState, context.Entry(sweet).State); Assert.Equal(expectedDependentState, dependentEntry.State); Assert.Equal(expectedDependentState, dependentEntry2a.State); @@ -302,6 +310,8 @@ public void Can_attach_owned_dependent_with_reference_to_parent(bool useAttach, Assert.Equal(4, context.ChangeTracker.Entries().Count()); + Assert.Equal(!setDependentKey, context.ChangeTracker.HasChanges()); + var dependentEntry = context.Entry(dreams); var dependentEntry2a = context.Entry(dreams.Are); var dependentEntry2b = context.Entry(dreams.Made); @@ -349,6 +359,8 @@ public void Can_attach_parent_with_child_collection() Assert.Equal(4, context.ChangeTracker.Entries().Count()); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(EntityState.Modified, context.Entry(category).State); Assert.Equal(EntityState.Modified, context.Entry(category.Products[0]).State); Assert.Equal(EntityState.Modified, context.Entry(category.Products[1]).State); @@ -378,6 +390,8 @@ public void Can_attach_child_with_reference_to_parent() Assert.Equal(2, context.ChangeTracker.Entries().Count()); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(EntityState.Modified, context.Entry(product).State); Assert.Equal(EntityState.Modified, context.Entry(product.Category).State); @@ -405,6 +419,8 @@ public void Can_attach_parent_with_one_to_one_children() Assert.Equal(3, context.ChangeTracker.Entries().Count()); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(EntityState.Unchanged, context.Entry(product).State); Assert.Equal(EntityState.Unchanged, context.Entry(product.Details).State); Assert.Equal(EntityState.Unchanged, context.Entry(product.Details.Tag).State); @@ -433,6 +449,8 @@ public void Can_attach_child_with_one_to_one_parents() Assert.Equal(3, context.ChangeTracker.Entries().Count()); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(EntityState.Unchanged, context.Entry(tag).State); Assert.Equal(EntityState.Unchanged, context.Entry(tag.Details).State); Assert.Equal(EntityState.Unchanged, context.Entry(tag.Details.Product).State); @@ -466,6 +484,8 @@ public void Can_attach_entity_with_one_to_one_parent_and_child() Assert.Equal(3, context.ChangeTracker.Entries().Count()); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(EntityState.Unchanged, context.Entry(details).State); Assert.Equal(EntityState.Unchanged, context.Entry(details.Product).State); Assert.Equal(EntityState.Unchanged, context.Entry(details.Tag).State); @@ -506,6 +526,8 @@ public void Entities_that_are_already_tracked_will_not_get_attached() Assert.Equal(4, context.ChangeTracker.Entries().Count()); + Assert.True(context.ChangeTracker.HasChanges()); + Assert.Equal(EntityState.Modified, context.Entry(category).State); Assert.Equal(EntityState.Modified, context.Entry(category.Products[0]).State); Assert.Equal(EntityState.Unchanged, context.Entry(category.Products[1]).State); @@ -574,6 +596,8 @@ public void Further_graph_traversal_stops_if_an_entity_is_not_attached() Assert.Equal(5, context.ChangeTracker.Entries().Count(e => e.State != EntityState.Detached)); + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(EntityState.Unchanged, context.Entry(category).State); Assert.Equal(EntityState.Unchanged, context.Entry(category.Products[0]).State); Assert.Equal(EntityState.Unchanged, context.Entry(category.Products[0].Details).State); @@ -610,6 +634,8 @@ public void Graph_iterator_does_not_go_visit_Apple() e => { })); Assert.Equal(0, context.ChangeTracker.Entries().Count(e => e.State != EntityState.Detached)); + + Assert.False(context.ChangeTracker.HasChanges()); } [ConditionalTheory] @@ -660,6 +686,8 @@ public void Can_add_owned_dependent_with_reference_to_parent(bool useAdd, bool s Assert.Equal(4, context.ChangeTracker.Entries().Count()); + Assert.True(context.ChangeTracker.HasChanges()); + var dependentEntry = context.Entry(dreams); var dependentEntry2a = context.Entry(dreams.Are); var dependentEntry2b = context.Entry(dreams.Made); @@ -700,6 +728,8 @@ public void Dependents_are_detached_not_deleted_when_principal_is_detached(bool context.Attach(category); + Assert.False(context.ChangeTracker.HasChanges()); + var categoryEntry = context.Entry(category); var product0Entry = context.Entry(category.Products[0]); var product1Entry = context.Entry(category.Products[1]); @@ -717,6 +747,8 @@ public void Dependents_are_detached_not_deleted_when_principal_is_detached(bool context.Entry(category).State = EntityState.Detached; + Assert.False(context.ChangeTracker.HasChanges()); + Assert.Equal(EntityState.Detached, categoryEntry.State); if (delayCascade) @@ -771,6 +803,8 @@ public void Dependents_are_detached_not_deleted_when_principal_is_detached(bool { Assert.Equal(4, context.ChangeTracker.Entries().Count()); + Assert.True(context.ChangeTracker.HasChanges()); + categoryEntry = context.Entry(newCategory); product0Entry = context.Entry(newCategory.Products[0]); product1Entry = context.Entry(newCategory.Products[1]); @@ -892,6 +926,8 @@ public void TrackGraph_overload_can_visit_a_graph_without_attaching() Assert.Equal(7, visited.Count); + Assert.False(context.ChangeTracker.HasChanges()); + foreach (var entity in new object[] { category } .Concat(category.Products) .Concat(category.Products.Select(e => e.Details))) @@ -961,6 +997,8 @@ private static void KeyValueAttachTest(string databaseName, Action