Skip to content

Commit 43bcc1f

Browse files
[release/10.0] Fix change tracker not detecting changes in nested complex collections (#37052)
Fixes #37026 --------- Co-authored-by: AndriySvyryd <[email protected]>
1 parent 3426320 commit 43bcc1f

File tree

2 files changed

+80
-1
lines changed

2 files changed

+80
-1
lines changed

src/EFCore/ChangeTracking/Internal/ChangeDetector.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ private bool LocalDetectChanges(InternalEntryBase entry)
283283
}
284284
}
285285

286-
foreach (var complexProperty in entry.StructuralType.GetComplexProperties())
286+
foreach (var complexProperty in entry.StructuralType.GetFlattenedComplexProperties())
287287
{
288288
if (complexProperty.IsCollection)
289289
{

test/EFCore.Tests/ChangeTracking/Internal/InternalComplexEntryTest.cs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,51 @@ public void GetEntry_throws_when_accessing_invalid_current_ordinal()
674674
Assert.Equal(CoreStrings.ComplexCollectionEntryOrdinalInvalid(5, "Blog", "Tags", 1), ex.Message);
675675
}
676676

677+
[ConditionalFact]
678+
public void DetectChanges_detects_changes_in_nested_complex_collections()
679+
{
680+
var model = CreateModelWithNestedComplexCollections();
681+
var entityType = model.FindEntityType(typeof(BlogWithNested))!;
682+
683+
var serviceProvider = InMemoryTestHelpers.Instance.CreateContextServices(model);
684+
var stateManager = serviceProvider.GetRequiredService<IStateManager>();
685+
var changeDetector = serviceProvider.GetRequiredService<IChangeDetector>();
686+
687+
var blog = new BlogWithNested
688+
{
689+
NestedJson = new NestedJson
690+
{
691+
Item = new NestedItem { Name = "foo" },
692+
Items =
693+
[
694+
new NestedItem { Name = "bar" },
695+
new NestedItem { Name = "baz" }
696+
]
697+
}
698+
};
699+
700+
var entityEntry = stateManager.GetOrCreateEntry(blog);
701+
entityEntry.SetEntityState(EntityState.Unchanged);
702+
703+
Assert.Equal(EntityState.Unchanged, entityEntry.EntityState);
704+
705+
// Replace the NestedJson with a new instance that has a modified Items collection
706+
blog.NestedJson = blog.NestedJson with
707+
{
708+
Items =
709+
[
710+
new NestedItem { Name = "bar" },
711+
new NestedItem { Name = "baz" },
712+
new NestedItem { Name = "new-bar" }
713+
]
714+
};
715+
716+
// DetectChanges should detect the change in the nested complex collection
717+
changeDetector.DetectChanges(stateManager);
718+
719+
Assert.Equal(EntityState.Modified, entityEntry.EntityState);
720+
}
721+
677722
private static IModel CreateModel()
678723
{
679724
var modelBuilder = InMemoryTestHelpers.Instance.CreateConventionBuilder();
@@ -688,6 +733,22 @@ private static IModel CreateModel()
688733
return modelBuilder.FinalizeModel();
689734
}
690735

736+
private static IModel CreateModelWithNestedComplexCollections()
737+
{
738+
var modelBuilder = InMemoryTestHelpers.Instance.CreateConventionBuilder();
739+
740+
modelBuilder.Entity<BlogWithNested>(eb =>
741+
{
742+
eb.ComplexProperty(e => e.NestedJson, b =>
743+
{
744+
b.ComplexProperty(a => a.Item);
745+
b.ComplexCollection(a => a.Items);
746+
});
747+
});
748+
749+
return modelBuilder.FinalizeModel();
750+
}
751+
691752
private class Blog
692753
{
693754
public int Id { get; set; }
@@ -714,4 +775,22 @@ private class Category
714775
public string Name { get; set; } = "";
715776
public string Description { get; set; } = "";
716777
}
778+
779+
private class BlogWithNested
780+
{
781+
public int Id { get; set; }
782+
public string Name { get; set; } = "";
783+
public NestedJson NestedJson { get; set; } = new();
784+
}
785+
786+
private record NestedJson
787+
{
788+
public NestedItem Item { get; init; } = new();
789+
public List<NestedItem> Items { get; init; } = [];
790+
}
791+
792+
private record NestedItem
793+
{
794+
public string Name { get; init; } = "";
795+
}
717796
}

0 commit comments

Comments
 (0)