diff --git a/src/EFCore/ChangeTracking/Internal/StateManager.cs b/src/EFCore/ChangeTracking/Internal/StateManager.cs index 2c08c24d8ec..347ebebfedd 100644 --- a/src/EFCore/ChangeTracking/Internal/StateManager.cs +++ b/src/EFCore/ChangeTracking/Internal/StateManager.cs @@ -671,6 +671,11 @@ public virtual void Clear() _needsUnsubscribe = false; SavingChanges = false; + + foreach (IResettableService set in ((IDbSetCache)Context).GetSets()) + { + set.ResetState(); + } } /// diff --git a/src/EFCore/ChangeTracking/LocalView.cs b/src/EFCore/ChangeTracking/LocalView.cs index b35ed9fa09d..4f57c167a36 100644 --- a/src/EFCore/ChangeTracking/LocalView.cs +++ b/src/EFCore/ChangeTracking/LocalView.cs @@ -273,7 +273,9 @@ public virtual bool Contains(TEntity item) { var entry = _context.GetDependencies().StateManager.TryGetEntry(item); - return entry != null && entry.EntityState != EntityState.Deleted; + return entry != null + && entry.EntityState != EntityState.Deleted + && entry.EntityState != EntityState.Detached; } /// diff --git a/src/EFCore/DbContext.cs b/src/EFCore/DbContext.cs index 172e2cfdbed..32fd68d0fa6 100644 --- a/src/EFCore/DbContext.cs +++ b/src/EFCore/DbContext.cs @@ -300,6 +300,16 @@ object IDbSetCache.GetOrAddSet(IDbSetSource source, string entityTypeName, Type return set; } + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [EntityFrameworkInternal] + IEnumerable IDbSetCache.GetSets() + => _sets?.Values ?? Enumerable.Empty(); + /// /// Creates a that can be used to query and save instances of . /// diff --git a/src/EFCore/Internal/IDbSetCache.cs b/src/EFCore/Internal/IDbSetCache.cs index ad9e3a90e27..6763d1a73a1 100644 --- a/src/EFCore/Internal/IDbSetCache.cs +++ b/src/EFCore/Internal/IDbSetCache.cs @@ -26,4 +26,12 @@ public interface IDbSetCache /// doing so can result in application failures when updating to a new Entity Framework Core release. /// object GetOrAddSet(IDbSetSource source, string entityTypeName, Type type); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + IEnumerable GetSets(); } diff --git a/test/EFCore.Specification.Tests/DatabindingTestBase.cs b/test/EFCore.Specification.Tests/DatabindingTestBase.cs index 48166d5078a..82ca45cc13c 100644 --- a/test/EFCore.Specification.Tests/DatabindingTestBase.cs +++ b/test/EFCore.Specification.Tests/DatabindingTestBase.cs @@ -256,13 +256,31 @@ public virtual void Entities_with_state_changed_to_detached_are_removed_from_loc Assert.Equal(TotalCount, local.Count); - foreach (var driver in context.Drivers.Local.Where(d => d.TeamId == UnchangedTeam).ToList()) + var drivers = context.Drivers.Local.Where(d => d.TeamId == UnchangedTeam).ToList(); + + foreach (var driver in drivers) { context.Entry(driver).State = EntityState.Detached; } Assert.Equal(0, local.Count(d => d.TeamId == UnchangedTeam)); Assert.Equal(TotalCount - UnchangedCount, local.Count); + foreach (var driver in drivers) + { + Assert.False(local.Contains(driver)); + } + + foreach (var driver in drivers) + { + Assert.Equal(EntityState.Detached, context.Entry(driver).State); + } + + Assert.Equal(0, local.Count(d => d.TeamId == UnchangedTeam)); + Assert.Equal(TotalCount - UnchangedCount, local.Count); + foreach (var driver in drivers) + { + Assert.False(local.Contains(driver)); + } } [ConditionalTheory] diff --git a/test/EFCore.Tests/ChangeTracking/ChangeTrackerTest.cs b/test/EFCore.Tests/ChangeTracking/ChangeTrackerTest.cs index 8d6fe491c98..c2a4a0bf1ea 100644 --- a/test/EFCore.Tests/ChangeTracking/ChangeTrackerTest.cs +++ b/test/EFCore.Tests/ChangeTracking/ChangeTrackerTest.cs @@ -3536,6 +3536,19 @@ public void Shadow_properties_are_not_included_in_update_unless_value_explicitly AssertValuesSaved(id, 0, null); } + [ConditionalFact] + public void Clearing_change_tracker_resets_local_view_count() + { + using var context = new LikeAZooContext(); + + int originalCount = context.Cats.Local.Count; + context.Cats.Add(new Cat(3)); + + context.ChangeTracker.Clear(); + + Assert.Equal(originalCount, context.Cats.Local.Count); + } + private static void AssertValuesSaved(int id, int someInt, string? someString) { using var context = new TheShadows();