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
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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use or pattern matching here:

Suggested change
if (value is IDelta || value is IDeltaSet)
if (value is IDelta or IDeltaSet)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's C# 9 feature and it seems we have to update the csproj to enable C# 9 language.
I don't want to introduce that changes in this PR. Thanks.

{
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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//If DeltaSet collection, we are handling delta collections so the value will be that itself and no need to get instance value
// 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,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of leaving a TODO in the code, I'd suggest adding a link to an issue in github documenting this feature.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#resolved.

// 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))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (!(deltaNestedResource is IDeltaSet))
if (deltaNestedResource is not IDeltaSet)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the above one. That's C# 9 feature and I 'd skip it in this PR. Thanks.

{
PropertyAccessor<T> cacheHit = _allProperties[name];
// Get the Delta<{NestedResourceType}>._instance using Reflection.
FieldInfo field = deltaNestedResource.GetType().GetField("_instance", BindingFlags.NonPublic | BindingFlags.Instance);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this was already like this in the previous code, but why do we use reflection on a type that we own? Or am I missing something here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think deltaNestedResource is a generic type instance, DeltaSet< T >. We don't know the real type until running.

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