Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions src/EFCore/ChangeTracking/Internal/OriginalPropertyValues.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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++)
Expand All @@ -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;
Expand Down
47 changes: 47 additions & 0 deletions test/EFCore.Tests/ChangeTracking/Internal/PropertyValuesTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Job>(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<bool?, SetValues?>))]
public void ToObject_with_null_complex_property_in_complex_collection_in_complex_collection(
Expand Down
Loading