From 05a45274605158a4b6d748f17f40843cbc10607e Mon Sep 17 00:00:00 2001 From: Tanzim Hossain Romel Date: Sat, 27 Jun 2026 02:53:49 +0600 Subject: [PATCH] Fix OriginalValues for added complex collections --- .../Internal/OriginalPropertyValues.cs | 17 +++++-- .../Internal/PropertyValuesTest.cs | 47 +++++++++++++++++++ 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/src/EFCore/ChangeTracking/Internal/OriginalPropertyValues.cs b/src/EFCore/ChangeTracking/Internal/OriginalPropertyValues.cs index 41263b6a718..ae79d5c44f5 100644 --- a/src/EFCore/ChangeTracking/Internal/OriginalPropertyValues.cs +++ b/src/EFCore/ChangeTracking/Internal/OriginalPropertyValues.cs @@ -71,7 +71,9 @@ protected override void SetValueInternal(IInternalEntry entry, IPropertyBase pro // The stored original collection contains references to the current CLR elements // (see SnapshotComplexCollection), so we must reconstruct each element from the - // per-entry original value snapshots to get true original values. + // per-entry original value snapshots to get true original values. For added entries, + // original values fall back to current values, so elements do not have original ordinals. + var useCurrentValues = entry.EntityState == EntityState.Added; var reconstructed = (IList)((IRuntimePropertyBase)complexProperty).GetIndexedCollectionAccessor() .Create(originalCollection.Count); for (var i = 0; i < originalCollection.Count; i++) @@ -83,14 +85,21 @@ protected override void SetValueInternal(IInternalEntry entry, IPropertyBase pro continue; } - var complexEntry = entry.GetComplexCollectionOriginalEntry(complexProperty, i); - if (!complexEntry.HasOriginalValuesSnapshot) + var complexEntry = useCurrentValues + ? entry.GetComplexCollectionEntry(complexProperty, i) + : entry.GetComplexCollectionOriginalEntry(complexProperty, i); + if (!useCurrentValues + && !complexEntry.HasOriginalValuesSnapshot) { complexEntry.EnsureOriginalValues(); SetValuesFromInstance(complexEntry, (IRuntimeTypeBase)complexProperty.ComplexType, element, skipChangeDetection: true); } - reconstructed.Add(new OriginalPropertyValues(complexEntry).Clone().ToObject()); + PropertyValues propertyValues = useCurrentValues + ? new CurrentPropertyValues(complexEntry) + : new OriginalPropertyValues(complexEntry); + + reconstructed.Add(propertyValues.Clone().ToObject()); } return reconstructed; diff --git a/test/EFCore.Tests/ChangeTracking/Internal/PropertyValuesTest.cs b/test/EFCore.Tests/ChangeTracking/Internal/PropertyValuesTest.cs index 597be722833..d2a005d2131 100644 --- a/test/EFCore.Tests/ChangeTracking/Internal/PropertyValuesTest.cs +++ b/test/EFCore.Tests/ChangeTracking/Internal/PropertyValuesTest.cs @@ -197,6 +197,53 @@ public void ToObject_with_complex_collection_containing_nested_nullable_complex_ Assert.Equal("505", result.Errors[2].InnerError!.InnerError!.Code); } + [Fact] + public void OriginalValues_ToObject_with_complex_collection_for_added_entity() + { + var job = new Job + { + Id = 1, + Name = "Added Job", + Errors = + [ + new RootJobError + { + Code = "500", + Message = "Server Error", + InnerErrors = + [ + new JobError { Code = "501", Message = "Not Implemented" } + ] + } + ] + }; + + var stateManager = CreateStateManager(BuildJobModel); + var internalEntry = stateManager.GetOrCreateEntry(job); + internalEntry.SetEntityState(EntityState.Added); + var entry = new EntityEntry(internalEntry); + + var result = (Job)entry.OriginalValues.ToObject(); + + Assert.NotSame(job, result); + Assert.Equal("Added Job", result.Name); + Assert.Single(result.Errors); + + var error = result.Errors[0]; + Assert.NotSame(job.Errors[0], error); + Assert.Equal("500", error.Code); + Assert.Equal("Server Error", error.Message); + Assert.Single(error.InnerErrors); + Assert.NotSame(job.Errors[0].InnerErrors[0], error.InnerErrors[0]); + Assert.Equal("501", error.InnerErrors[0].Code); + + error.Code = "Changed"; + error.InnerErrors[0].Code = "Changed"; + + Assert.Equal("500", job.Errors[0].Code); + Assert.Equal("501", job.Errors[0].InnerErrors[0].Code); + } + [Theory] [ClassData(typeof(DataGenerator))] public void ToObject_with_null_complex_property_in_complex_collection_in_complex_collection(