Skip to content

Commit

Permalink
Serialize navigation properties in Deep insert responses (#2674)
Browse files Browse the repository at this point in the history
  • Loading branch information
KenitoInc authored Sep 27, 2022
1 parent 8c04c87 commit 728ba3d
Show file tree
Hide file tree
Showing 12 changed files with 267 additions and 91 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class EdmChangedObjectCollection : Collection<IEdmChangedObject>, IEdmObj
private IEdmEntityType _entityType;
private EdmDeltaCollectionType _edmType;
private IEdmCollectionTypeReference _edmTypeReference;

/// <summary>
/// Initializes a new instance of the <see cref="EdmChangedObjectCollection"/> class.
/// </summary>
Expand Down Expand Up @@ -71,7 +71,6 @@ private void Initialize(IEdmEntityType entityType)
_entityType = entityType;
_edmType = new EdmDeltaCollectionType(new EdmEntityTypeReference(_entityType, isNullable: true));
_edmTypeReference = new EdmCollectionTypeReference(_edmType);

}

/// <summary>
Expand Down Expand Up @@ -102,7 +101,7 @@ internal EdmChangedObjectCollection Patch(EdmODataAPIHandler apiHandler, ODataEd
internal EdmChangedObjectCollection CopyChangedValues(EdmODataAPIHandler apiHandler, ODataEdmAPIHandlerFactory apiHandlerFactory = null)
{
EdmChangedObjectCollection changedObjectCollection = new EdmChangedObjectCollection(_entityType);
string[] keys = _entityType.Key().Select(x=>x.Name).ToArray();
string[] keys = _entityType.Key().Select(x => x.Name).ToArray();

foreach (IEdmChangedObject changedObj in Items)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,7 @@ public virtual object ReadResource(ODataResourceWrapper resourceWrapper, IEdmStr
{
throw Error.ArgumentNull("readContext");
}



if (!String.IsNullOrEmpty(resourceWrapper.ResourceBase.TypeName) && structuredType.FullName() != resourceWrapper.ResourceBase.TypeName)
{
// received a derived type in a base type deserializer. delegate it to the appropriate derived type deserializer.
Expand Down Expand Up @@ -835,7 +834,6 @@ public virtual void ApplyStructuralProperty(object resource, ODataProperty struc

private void ApplyResourceProperties(object resource, ODataResourceWrapper resourceWrapper,
IEdmStructuredTypeReference structuredType, ODataDeserializerContext readContext)

{
ApplyStructuralProperties(resource, resourceWrapper, structuredType, readContext);
ApplyNestedProperties(resource, resourceWrapper, structuredType, readContext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq;
Expand Down Expand Up @@ -647,6 +648,7 @@ private void WriteResource(object graph, ODataWriter writer, ODataSerializerCont
WriteDynamicComplexProperties(resourceContext, writer);
WriteNavigationLinks(selectExpandNode, resourceContext, writer);
WriteExpandedNavigationProperties(selectExpandNode, resourceContext, writer);
WriteNestedNavigationProperties(selectExpandNode, resourceContext, writer);
WriteReferencedNavigationProperties(selectExpandNode, resourceContext, writer);
writer.WriteEnd();
}
Expand Down Expand Up @@ -689,6 +691,7 @@ await writer.WriteEntityReferenceLinkAsync(new ODataEntityReferenceLink
await WriteDynamicComplexPropertiesAsync(resourceContext, writer);
await WriteNavigationLinksAsync(selectExpandNode, resourceContext, writer);
await WriteExpandedNavigationPropertiesAsync(selectExpandNode, resourceContext, writer);
await WriteNestedNavigationPropertiesAsync(selectExpandNode, resourceContext, writer);
await WriteReferencedNavigationPropertiesAsync(selectExpandNode, resourceContext, writer);
await writer.WriteEndAsync();
}
Expand Down Expand Up @@ -1118,7 +1121,7 @@ private void WriteNavigationLinks(SelectExpandNode selectExpandNode, ResourceCon
Contract.Assert(selectExpandNode != null);
Contract.Assert(resourceContext != null);

if (selectExpandNode.SelectedNavigationProperties == null)
if (selectExpandNode.SelectedNavigationProperties == null || resourceContext.IsPostRequest)
{
return;
}
Expand All @@ -1139,7 +1142,7 @@ private async Task WriteNavigationLinksAsync(SelectExpandNode selectExpandNode,
Contract.Assert(selectExpandNode != null);
Contract.Assert(resourceContext != null);

if (selectExpandNode.SelectedNavigationProperties == null)
if (selectExpandNode.SelectedNavigationProperties == null || resourceContext.IsPostRequest)
{
return;
}
Expand Down Expand Up @@ -1376,7 +1379,8 @@ private IEnumerable<KeyValuePair<IEdmStructuralProperty, PathSelectItem>> GetPro
if (complexProperties != null)
{
IEnumerable<string> changedProperties = null;
if (null != resourceContext.EdmObject && resourceContext.EdmObject.IsDeltaResource())

if (resourceContext.EdmObject != null && resourceContext.EdmObject.IsDeltaResource())
{
IDelta deltaObject = resourceContext.EdmObject as IDelta;
changedProperties = deltaObject.GetChangedPropertyNames();
Expand All @@ -1396,35 +1400,66 @@ private IEnumerable<KeyValuePair<IEdmNavigationProperty, Type>> GetNavigationPro
{
ISet<IEdmNavigationProperty> navigationProperties = selectExpandNode.SelectedNavigationProperties;

if (navigationProperties != null)
if (navigationProperties == null)
{
IEnumerable<string> changedProperties = null;
if (null != resourceContext.EdmObject && resourceContext.EdmObject is IDelta changedObject)
yield break;
}

if (resourceContext.EdmObject is IDelta changedObject)
{
IEnumerable<string> changedProperties = changedObject.GetChangedPropertyNames();

foreach (IEdmNavigationProperty navigationProperty in navigationProperties)
{
changedProperties = changedObject.GetChangedPropertyNames();
if (changedProperties != null && changedProperties.Contains(navigationProperty.Name))
{
yield return new KeyValuePair<IEdmNavigationProperty, Type>(navigationProperty, typeof(IEdmChangedObject));
}
}
}
else if (resourceContext.ResourceInstance is IDelta deltaObject)
{
IEnumerable<string> changedProperties = deltaObject.GetChangedPropertyNames();
dynamic delta = deltaObject;

foreach (IEdmNavigationProperty navigationProperty in navigationProperties)
{
object obj = null;

foreach (IEdmNavigationProperty navigationProperty in navigationProperties)
if (changedProperties != null && changedProperties.Contains(navigationProperty.Name) && delta.DeltaNestedResources.TryGetValue(navigationProperty.Name, out obj))
{
if (changedProperties == null || changedProperties.Contains(navigationProperty.Name))
if (obj != null)
{
yield return new KeyValuePair<IEdmNavigationProperty, Type>(navigationProperty, typeof(IEdmChangedObject));
yield return new KeyValuePair<IEdmNavigationProperty, Type>(navigationProperty, obj.GetType());
}
}
}
else if (null != resourceContext.ResourceInstance && resourceContext.ResourceInstance is IDelta deltaObject)
}
// Serializing nested navigation properties from a deep insert request.
// We currently don't deserialize Deep insert nested resources as Delta<T> but as T. If this was to change in the future, logic in this method will have to change.
else if (resourceContext.IsPostRequest)
{
object instance = resourceContext.ResourceInstance;
PropertyInfo[] properties = instance.GetType().GetProperties();
Dictionary<string, object> propertyNamesAndValues = new Dictionary<string, object>();

foreach (PropertyInfo propertyInfo in properties)
{
changedProperties = deltaObject.GetChangedPropertyNames();
dynamic delta = deltaObject;
string name = propertyInfo.Name;
object value = propertyInfo.GetValue(instance);
propertyNamesAndValues.Add(name, value);
}

foreach (IEdmNavigationProperty navigationProperty in navigationProperties)
foreach (IEdmNavigationProperty navigationProperty in navigationProperties)
{
if (propertyNamesAndValues.TryGetValue(navigationProperty.Name, out object obj))
{
Object obj = null;
if (changedProperties == null || changedProperties.Contains(navigationProperty.Name) && delta.DeltaNestedResources.TryGetValue(navigationProperty.Name, out obj))
if (obj != null)
{
yield return new KeyValuePair<IEdmNavigationProperty, Type>(navigationProperty, obj.GetType());
}
}
}
}
}
}

Expand Down Expand Up @@ -1581,7 +1616,7 @@ private async Task WriteComplexAndExpandedNavigationPropertyAsync(IEdmProperty e

object propertyValue = resourceContext.GetPropertyValue(edmProperty.Name);

if (propertyValue == null || propertyValue is NullEdmComplexObject)
if (propertyValue == null || propertyValue is NullEdmComplexObject || propertyValue is ODataIdContainer)
{
if (edmProperty.Type.IsCollection())
{
Expand Down Expand Up @@ -1618,6 +1653,58 @@ await writer.WriteStartAsync(new ODataResourceSet
}
}

private void WriteNestedNavigationProperties(SelectExpandNode selectExpandNode, ResourceContext resourceContext, ODataWriter writer)
{
Debug.Assert(resourceContext != null, "resourceContext != null");
Debug.Assert(writer != null, "writer != null");

if (!resourceContext.IsPostRequest)
{
return;
}

IEnumerable<KeyValuePair<IEdmNavigationProperty, Type>> navigationProperties = GetNavigationPropertiesToWrite(selectExpandNode, resourceContext);

foreach (KeyValuePair<IEdmNavigationProperty, Type> navigationProperty in navigationProperties)
{
ODataNestedResourceInfo nestedResourceInfo = new ODataNestedResourceInfo
{
IsCollection = navigationProperty.Key.Type.IsCollection(),
Name = navigationProperty.Key.Name
};

writer.WriteStart(nestedResourceInfo);
WriteComplexAndExpandedNavigationProperty(navigationProperty.Key, null, resourceContext, writer);
writer.WriteEnd();
}
}

private async Task WriteNestedNavigationPropertiesAsync(SelectExpandNode selectExpandNode, ResourceContext resourceContext, ODataWriter writer)
{
Debug.Assert(resourceContext != null, "resourceContext != null");
Debug.Assert(writer != null, "writer != null");

if (!resourceContext.IsPostRequest)
{
return;
}

IEnumerable<KeyValuePair<IEdmNavigationProperty, Type>> navigationProperties = GetNavigationPropertiesToWrite(selectExpandNode, resourceContext);

foreach (KeyValuePair<IEdmNavigationProperty, Type> navigationProperty in navigationProperties)
{
ODataNestedResourceInfo nestedResourceInfo = new ODataNestedResourceInfo
{
IsCollection = navigationProperty.Key.Type.IsCollection(),
Name = navigationProperty.Key.Name
};

await writer.WriteStartAsync(nestedResourceInfo);
await WriteComplexAndExpandedNavigationPropertyAsync(navigationProperty.Key, null, resourceContext, writer);
await writer.WriteEndAsync();
}
}

private IEnumerable<ODataNestedResourceInfo> CreateNavigationLinks(
IEnumerable<IEdmNavigationProperty> navigationProperties, ResourceContext resourceContext)
{
Expand Down Expand Up @@ -1741,7 +1828,7 @@ private IEnumerable<ODataProperty> CreateStructuralPropertyBag(SelectExpandNode
{
IEnumerable<IEdmStructuralProperty> structuralProperties = selectExpandNode.SelectedStructuralProperties;

if (null != resourceContext.EdmObject && resourceContext.EdmObject.IsDeltaResource())
if (resourceContext.EdmObject != null && resourceContext.EdmObject.IsDeltaResource())
{
IDelta deltaObject = resourceContext.EdmObject as IDelta;
IEnumerable<string> changedProperties = deltaObject.GetChangedPropertyNames();
Expand Down
12 changes: 12 additions & 0 deletions src/Microsoft.AspNet.OData.Shared/ResourceContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,18 @@ public IEdmModel EdmModel
}
}

internal bool IsPostRequest
{
get
{
#if NETCORE
return Request == null ? false : String.Equals(Request.Method, "post", StringComparison.OrdinalIgnoreCase);
#else
return Request == null ? false : String.Equals(Request.Method.ToString(), "post", StringComparison.OrdinalIgnoreCase);
#endif
}
}

/// <summary>
/// Gets or sets the <see cref="IEdmNavigationSource"/> to which this instance belongs.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.AspNet.OData/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@
[assembly: SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Scope = "member", Target = "Microsoft.AspNet.OData.Common.Error.#PropertyNullOrWhiteSpace()")]

[assembly: SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Scope = "member", Target = "Microsoft.AspNet.OData.Delta`1.#CopyChangedValues(!0)")]

[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "Microsoft.AspNet.OData.Delta`1.#Microsoft.AspNet.OData.IEdmObject.GetEdmType()")]
[assembly: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Upsert", Scope = "member", Target = "Microsoft.AspNet.OData.DataModificationOperationKind.#Upsert")]
[assembly: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Upsert", Scope = "member", Target = "Org.OData.Core.V1.DataModificationOperationKind.#Upsert")]
Expand Down Expand Up @@ -121,6 +120,7 @@
[assembly: SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Scope = "member", Target = "Microsoft.AspNet.OData.ODataAPIHandler`1.#TryGet(System.Collections.Generic.IDictionary`2<System.String,System.Object>,!0&,System.String&)")]
[assembly: SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#", Scope = "member", Target = "Microsoft.AspNet.OData.ODataAPIHandler`1.#TryDelete(System.Collections.Generic.IDictionary`2<System.String,System.Object>,System.String&)")]
[assembly: SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#", Scope = "member", Target = "Microsoft.AspNet.OData.ODataAPIHandler`1.#TryGet(System.Collections.Generic.IDictionary`2<System.String,System.Object>,!0&,System.String&)")]
[assembly: SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Scope = "member", Target = "Microsoft.AspNet.OData.ODataAPIHandler`1.#GetODataPath(System.String,Microsoft.OData.Edm.IEdmModel)")]
[assembly: SuppressMessage("Microsoft.Design", "CA1040:AvoidEmptyInterfaces", Scope = "type", Target = "Microsoft.AspNet.OData.IODataAPIHandler")]
[assembly: SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Scope = "member", Target = "Microsoft.AspNet.OData.Formatter.Serialization.DefaultODataSerializerProvider.#GetODataPayloadSerializerImpl(System.Type,System.Func`1<Microsoft.OData.Edm.IEdmModel>,Microsoft.AspNet.OData.Routing.ODataPath,System.Type)")]
[assembly: SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#", Scope = "member", Target = "Microsoft.AspNet.OData.EdmODataAPIHandler.#TryDelete(System.Collections.Generic.IDictionary`2<System.String,System.Object>,System.String&)")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@
<Compile Include="..\Aggregation\AggregationTests.cs">
<Link>Aggregation\AggregationTests.cs</Link>
</Compile>
<Compile Include="..\BulkOperation\BulkOperationPatchHandlers.cs">
<Link>BulkOperation\BulkOperationPatchHandlers.cs</Link>
<Compile Include="..\BulkOperation\BulkOperationTest.cs">
<Link>BulkOperation\BulkOperationTest.cs</Link>
</Compile>
<Compile Include="..\BulkOperation\BulkOperationDataModel.cs">
<Link>BulkOperation\BulkOperationDataModel.cs</Link>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

namespace Microsoft.Test.E2E.AspNet.OData.BulkOperation
{
[AutoExpand]
public class Employee
{
[Key]
Expand Down
Loading

0 comments on commit 728ba3d

Please sign in to comment.