From 43bcc1fe0cab9c999e34d15c0ca93daac6f2b386 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 3 Nov 2025 12:25:23 -0800 Subject: [PATCH 1/2] [release/10.0] Fix change tracker not detecting changes in nested complex collections (#37052) Fixes #37026 --------- Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com> --- .../ChangeTracking/Internal/ChangeDetector.cs | 2 +- .../Internal/InternalComplexEntryTest.cs | 79 +++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/src/EFCore/ChangeTracking/Internal/ChangeDetector.cs b/src/EFCore/ChangeTracking/Internal/ChangeDetector.cs index d9d8dea9f5f..042e8744a7d 100644 --- a/src/EFCore/ChangeTracking/Internal/ChangeDetector.cs +++ b/src/EFCore/ChangeTracking/Internal/ChangeDetector.cs @@ -283,7 +283,7 @@ private bool LocalDetectChanges(InternalEntryBase entry) } } - foreach (var complexProperty in entry.StructuralType.GetComplexProperties()) + foreach (var complexProperty in entry.StructuralType.GetFlattenedComplexProperties()) { if (complexProperty.IsCollection) { diff --git a/test/EFCore.Tests/ChangeTracking/Internal/InternalComplexEntryTest.cs b/test/EFCore.Tests/ChangeTracking/Internal/InternalComplexEntryTest.cs index f0089b8d870..ef361b1a753 100644 --- a/test/EFCore.Tests/ChangeTracking/Internal/InternalComplexEntryTest.cs +++ b/test/EFCore.Tests/ChangeTracking/Internal/InternalComplexEntryTest.cs @@ -674,6 +674,51 @@ public void GetEntry_throws_when_accessing_invalid_current_ordinal() Assert.Equal(CoreStrings.ComplexCollectionEntryOrdinalInvalid(5, "Blog", "Tags", 1), ex.Message); } + [ConditionalFact] + public void DetectChanges_detects_changes_in_nested_complex_collections() + { + var model = CreateModelWithNestedComplexCollections(); + var entityType = model.FindEntityType(typeof(BlogWithNested))!; + + var serviceProvider = InMemoryTestHelpers.Instance.CreateContextServices(model); + var stateManager = serviceProvider.GetRequiredService(); + var changeDetector = serviceProvider.GetRequiredService(); + + var blog = new BlogWithNested + { + NestedJson = new NestedJson + { + Item = new NestedItem { Name = "foo" }, + Items = + [ + new NestedItem { Name = "bar" }, + new NestedItem { Name = "baz" } + ] + } + }; + + var entityEntry = stateManager.GetOrCreateEntry(blog); + entityEntry.SetEntityState(EntityState.Unchanged); + + Assert.Equal(EntityState.Unchanged, entityEntry.EntityState); + + // Replace the NestedJson with a new instance that has a modified Items collection + blog.NestedJson = blog.NestedJson with + { + Items = + [ + new NestedItem { Name = "bar" }, + new NestedItem { Name = "baz" }, + new NestedItem { Name = "new-bar" } + ] + }; + + // DetectChanges should detect the change in the nested complex collection + changeDetector.DetectChanges(stateManager); + + Assert.Equal(EntityState.Modified, entityEntry.EntityState); + } + private static IModel CreateModel() { var modelBuilder = InMemoryTestHelpers.Instance.CreateConventionBuilder(); @@ -688,6 +733,22 @@ private static IModel CreateModel() return modelBuilder.FinalizeModel(); } + private static IModel CreateModelWithNestedComplexCollections() + { + var modelBuilder = InMemoryTestHelpers.Instance.CreateConventionBuilder(); + + modelBuilder.Entity(eb => + { + eb.ComplexProperty(e => e.NestedJson, b => + { + b.ComplexProperty(a => a.Item); + b.ComplexCollection(a => a.Items); + }); + }); + + return modelBuilder.FinalizeModel(); + } + private class Blog { public int Id { get; set; } @@ -714,4 +775,22 @@ private class Category public string Name { get; set; } = ""; public string Description { get; set; } = ""; } + + private class BlogWithNested + { + public int Id { get; set; } + public string Name { get; set; } = ""; + public NestedJson NestedJson { get; set; } = new(); + } + + private record NestedJson + { + public NestedItem Item { get; init; } = new(); + public List Items { get; init; } = []; + } + + private record NestedItem + { + public string Name { get; init; } = ""; + } } From 46842f5ecf9aa339f76aa23ea507900c58b543d5 Mon Sep 17 00:00:00 2001 From: Andriy Svyryd Date: Mon, 3 Nov 2025 14:26:37 -0600 Subject: [PATCH 2/2] Remove auditSources from NuGet.config (#37053) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alexander Köplinger --- NuGet.config | 4 ---- 1 file changed, 4 deletions(-) diff --git a/NuGet.config b/NuGet.config index b8e45392bc6..11bcc2d5e56 100644 --- a/NuGet.config +++ b/NuGet.config @@ -19,10 +19,6 @@ - - - -