Skip to content

Commit

Permalink
Enable the nested delta collection deep update (#747)
Browse files Browse the repository at this point in the history
* Enable the nested delta collection deep update

* Address the comments
  • Loading branch information
xuzhg authored Dec 1, 2022
1 parent b3749a8 commit a273006
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 11 deletions.
32 changes: 26 additions & 6 deletions src/Microsoft.AspNetCore.OData/Deltas/DeltaOfT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ public override bool TrySetPropertyValue(string name, object value)
}
}

if (value is IDelta)
if (value is IDelta || value is IDeltaSet)
{
return TrySetNestedResourceInternal(name, value);
}
Expand Down Expand Up @@ -225,6 +225,14 @@ internal bool TryGetNestedPropertyValue(string name, out object value)
object deltaNestedResource = _deltaNestedResources[name];

Contract.Assert(deltaNestedResource != null, "deltaNestedResource != null");

//If DeltaSet collection, we are handling delta collections so the value will be that itself and no need to get instance value
if (deltaNestedResource is IDeltaSet)
{
value = deltaNestedResource;
return true;
}

Contract.Assert(DeltaHelper.IsDeltaOfT(deltaNestedResource.GetType()));

value = deltaNestedResource;
Expand Down Expand Up @@ -339,6 +347,15 @@ public void CopyChangedValues(T original)
{
// Patch for each nested resource changed under this T.
dynamic deltaNestedResource = _deltaNestedResources[nestedResourceName];

if (deltaNestedResource is IDeltaSet)
{
// TODO: That's the bulk insert OData Path handler feature,
// See the comments in https://github.com/OData/AspNetCoreOData/issues/748
// So far, Let's skip DeltaSet and figure it out later.
continue;
}

dynamic originalNestedResource = null;
if (!TryGetPropertyRef(original, nestedResourceName, out originalNestedResource))
{
Expand Down Expand Up @@ -705,11 +722,14 @@ private bool TrySetNestedResourceInternal(string name, object deltaNestedResourc
return false;
}

PropertyAccessor<T> cacheHit = _allProperties[name];
// Get the Delta<{NestedResourceType}>._instance using Reflection.
FieldInfo field = deltaNestedResource.GetType().GetField("_instance", BindingFlags.NonPublic | BindingFlags.Instance);
Contract.Assert(field != null, "field != null");
cacheHit.SetValue(_instance, field.GetValue(deltaNestedResource));
if (!(deltaNestedResource is IDeltaSet))
{
PropertyAccessor<T> cacheHit = _allProperties[name];
// Get the Delta<{NestedResourceType}>._instance using Reflection.
FieldInfo field = deltaNestedResource.GetType().GetField("_instance", BindingFlags.NonPublic | BindingFlags.Instance);
Contract.Assert(field != null, "field != null");
cacheHit.SetValue(_instance, field.GetValue(deltaNestedResource));
}

// Add the nested resource in the hierarchy.
// Note: We shouldn't add the structural properties to the <code>_changedProperties</code>, which
Expand Down
32 changes: 32 additions & 0 deletions src/Microsoft.AspNetCore.OData/Edm/EdmModelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,38 @@ namespace Microsoft.AspNetCore.OData.Edm
{
internal static class EdmModelExtensions
{
/// <summary>
/// Get all property names for the given structured type.
/// </summary>
/// <param name="model">The Edm model.</param>
/// <param name="structuredType">The given structured type.</param>
/// <returns>All property names.</returns>
public static ICollection<string> GetAllProperties(this IEdmModel model, IEdmStructuredType structuredType)
{
if (model == null)
{
throw Error.ArgumentNull(nameof(model));
}

if (structuredType == null)
{
throw Error.ArgumentNull(nameof(structuredType));
}

IList<string> allProperties = new List<string>();
foreach (var property in structuredType.StructuralProperties())
{
allProperties.Add(model.GetClrPropertyName(property));
}

foreach (var property in structuredType.NavigationProperties())
{
allProperties.Add(model.GetClrPropertyName(property));
}

return allProperties;
}

/// <summary>
/// Resolve the alternate key properties.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,13 @@ internal static void SetCollectionProperty(object resource, string propertyName,
{
if (value != null)
{
// If the setting value is a delta set, we don't need to create a new collection, just use it.
if (value is IDeltaSet set)
{
SetProperty(resource, propertyName, set);
return;
}

IEnumerable collection = value as IEnumerable;
Contract.Assert(collection != null,
"SetCollectionProperty is always passed the result of ODataFeedDeserializer or ODataCollectionDeserializer");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,20 +263,19 @@ public virtual object CreateResourceInstance(IEdmStructuredTypeReference structu

if (readContext.IsDeltaOfT || readContext.IsDeltaDeleted)
{
IEnumerable<string> structuralProperties = structuredType.StructuralProperties()
.Select(edmProperty => model.GetClrPropertyName(edmProperty));
IEnumerable<string> updatablePoperties = model.GetAllProperties(structuredType.StructuredDefinition());

if (structuredType.IsOpen())
{
PropertyInfo dynamicDictionaryPropertyInfo = model.GetDynamicPropertyDictionary(
structuredType.StructuredDefinition());

return Activator.CreateInstance(readContext.ResourceType, clrType, structuralProperties,
return Activator.CreateInstance(readContext.ResourceType, clrType, updatablePoperties,
dynamicDictionaryPropertyInfo);
}
else
{
return Activator.CreateInstance(readContext.ResourceType, clrType, structuralProperties);
return Activator.CreateInstance(readContext.ResourceType, clrType, updatablePoperties);
}
}
else
Expand Down
8 changes: 8 additions & 0 deletions src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2221,6 +2221,14 @@
<param name="entityType">The Edm entity type.</param>
<returns>Alternate Keys of this type.</returns>
</member>
<member name="M:Microsoft.AspNetCore.OData.Edm.EdmModelExtensions.GetAllProperties(Microsoft.OData.Edm.IEdmModel,Microsoft.OData.Edm.IEdmStructuredType)">
<summary>
Get all property names for the given structured type.
</summary>
<param name="model">The Edm model.</param>
<param name="structuredType">The given structured type.</param>
<returns>All property names.</returns>
</member>
<member name="M:Microsoft.AspNetCore.OData.Edm.EdmModelExtensions.ResolveAlternateKeyProperties(Microsoft.OData.Edm.IEdmModel,Microsoft.OData.UriParser.KeySegment)">
<summary>
Resolve the alternate key properties.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,7 @@ public void CreateResourceInstance_CreatesDeltaWith_ExpectedUpdatableProperties(
Model = _readContext.Model,
ResourceType = typeof(Delta<Product>)
};
var structuralProperties = _productEdmType.StructuralProperties().Select(p => p.Name);
var structuralProperties = _readContext.Model.GetAllProperties(_productEdmType.StructuredDefinition());

// Act
Delta<Product> resource = deserializer.CreateResourceInstance(_productEdmType, readContext) as Delta<Product>;
Expand Down

0 comments on commit a273006

Please sign in to comment.