Skip to content
Open
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
68 changes: 61 additions & 7 deletions src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ public class ArrayPropertyValues : PropertyValues
private readonly List<ArrayPropertyValues?>?[] _complexCollectionValues;
private readonly bool[]? _nullComplexPropertyFlags;

private static readonly bool UseOldBehavior37516 =
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue37516", out var enabled) && enabled;

/// <summary>
/// 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
Expand Down Expand Up @@ -50,7 +53,7 @@ public override object ToObject()
if (_nullComplexPropertyFlags[i])
{
var complexProperty = NullableComplexProperties[i];
structuralObject = ((IRuntimeComplexProperty)complexProperty).GetSetter().SetClrValue(structuralObject, null);
structuralObject = SetNestedComplexPropertyValue(structuralObject, complexProperty, null);
}
}
}
Expand All @@ -68,7 +71,7 @@ public override object ToObject()
!complexProperty.IsShadowProperty(),
$"Shadow complex property {complexProperty.Name} is not supported. Issue #31243");
var list = (IList)((IRuntimeComplexProperty)complexProperty).GetIndexedCollectionAccessor().Create(propertyValuesList.Count);
structuralObject = ((IRuntimeComplexProperty)complexProperty).GetSetter().SetClrValue(structuralObject, list);
structuralObject = SetNestedComplexPropertyValue(structuralObject, complexProperty, list);

foreach (var propertyValues in propertyValuesList)
{
Expand All @@ -79,6 +82,33 @@ public override object ToObject()
return structuralObject;
}

private object SetNestedComplexPropertyValue(object structuralObject, IComplexProperty complexProperty, object? value)
{
return UseOldBehavior37516
? ((IRuntimeComplexProperty)complexProperty).GetSetter().SetClrValue(structuralObject, value)
: SetValueRecursively(structuralObject, complexProperty.GetChainToComplexProperty(fromEntity: false), 0, value);

static object SetValueRecursively(object instance, IReadOnlyList<IComplexProperty> chain, int index, object? value)
{
var currentProperty = (IRuntimeComplexProperty)chain[index];
if (index == chain.Count - 1)
{
return currentProperty.GetSetter().SetClrValue(instance, value);
}

var child = currentProperty.GetGetter().GetClrValue(instance);
if (child == null)
{
return instance;
}

var updated = SetValueRecursively(child, chain, index + 1, value);
// Need to update the child value as well because it could be a value type
// TODO: Improve this, see #36041
return currentProperty.GetSetter().SetClrValue(instance, updated);
}
}

/// <summary>
/// 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
Expand Down Expand Up @@ -495,20 +525,44 @@ ArrayPropertyValues CreateComplexPropertyValues(object complexObject, InternalCo
for (var i = 0; i < properties.Count; i++)
{
var property = properties[i];
var getter = property.GetGetter();
values[i] = getter.GetClrValue(complexObject);
var targetObject = NavigateToDeclaringType(complexObject, property.DeclaringType, complexType);
values[i] = targetObject == null ? null : property.GetGetter().GetClrValue(targetObject);
}

var complexPropertyValues = new ArrayPropertyValues(entry, values, null);

foreach (var nestedComplexProperty in complexPropertyValues.ComplexCollectionProperties)
{
var nestedCollection = (IList?)nestedComplexProperty.GetGetter().GetClrValue(complexObject);
var propertyValuesList = GetComplexCollectionPropertyValues(nestedComplexProperty, nestedCollection);
complexPropertyValues.SetComplexCollectionValue(nestedComplexProperty, propertyValuesList);
var targetObject = NavigateToDeclaringType(complexObject, nestedComplexProperty.DeclaringType, complexType);
var nestedCollection = targetObject == null ? null : (IList?)nestedComplexProperty.GetGetter().GetClrValue(targetObject);
var nestedPropertyValuesList = GetComplexCollectionPropertyValues(nestedComplexProperty, nestedCollection);
complexPropertyValues.SetComplexCollectionValue(nestedComplexProperty, nestedPropertyValuesList);
}

return complexPropertyValues;
}

static object? NavigateToDeclaringType(object root, ITypeBase declaringType, IRuntimeTypeBase rootType)
{
if (declaringType == rootType
|| UseOldBehavior37516)
{
return root;
}

if (declaringType is not IComplexType ct)
{
return root;
}

var chain = ct.ComplexProperty.GetChainToComplexProperty(fromEntity: false);
object? target = root;
for (var i = 0; i < chain.Count && target != null; i++)
{
target = chain[i].GetGetter().GetClrValue(target);
}

return target;
}
}
}
Loading