Skip to content

Commit a6c6763

Browse files
ElizabethOkerioSreejithpin
authored andcommitted
Feature/new add support for odata bind (#2506)
* support for odata.bind * support for odata.bind
1 parent 1c83dd5 commit a6c6763

File tree

6 files changed

+634
-20
lines changed

6 files changed

+634
-20
lines changed

src/Microsoft.AspNet.OData.Shared/Formatter/Deserialization/ODataEntityReferenceLinkBase.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,13 @@ public class ODataEntityReferenceLinkBase : ODataItemBase
1717
public ODataEntityReferenceLinkBase(ODataEntityReferenceLink item)
1818
: base(item)
1919
{
20+
EntityReferenceLink = item;
2021
}
2122

2223
/// <summary>
2324
/// Gets the wrapped <see cref="ODataEntityReferenceLink"/>.
2425
/// </summary>
25-
public ODataEntityReferenceLink EntityReferenceLink
26-
{
27-
get
28-
{
29-
return Item as ODataEntityReferenceLink;
30-
}
31-
}
26+
public ODataEntityReferenceLink EntityReferenceLink { get; }
27+
3228
}
3329
}

src/Microsoft.AspNet.OData.Shared/Formatter/Deserialization/ODataNestedResourceInfoWrapper.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,6 @@ public ODataNestedResourceInfo NestedResourceInfo
4040
/// Gets the nested items that are part of this nested resource info.
4141
/// </summary>
4242
public IList<ODataItemBase> NestedItems { get; private set; }
43+
4344
}
4445
}

src/Microsoft.AspNet.OData.Shared/Formatter/Deserialization/ODataResourceDeserializer.cs

Lines changed: 172 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@
1111
using System.Runtime.Serialization;
1212
using System.Threading.Tasks;
1313
using Microsoft.AspNet.OData.Common;
14-
using Microsoft.AspNet.OData.Formatter.Serialization;
14+
using Microsoft.AspNet.OData.Interfaces;
15+
using Microsoft.AspNet.OData.Routing;
1516
using Microsoft.OData;
1617
using Microsoft.OData.Edm;
18+
using Microsoft.OData.UriParser;
19+
using ODataPath = Microsoft.AspNet.OData.Routing.ODataPath;
1720

1821
namespace Microsoft.AspNet.OData.Formatter.Deserialization
1922
{
@@ -97,6 +100,7 @@ public sealed override object ReadInline(object item, IEdmTypeReference edmType,
97100

98101
// Recursion guard to avoid stack overflows
99102
RuntimeHelpers.EnsureSufficientExecutionStack();
103+
resourceWrapper = UpdateResourceWrapper(resourceWrapper, readContext);
100104

101105
return ReadResource(resourceWrapper, edmType.AsStructured(), readContext);
102106
}
@@ -331,7 +335,39 @@ public virtual void ApplyNestedProperty(object resource, ODataNestedResourceInfo
331335
}
332336
}
333337

334-
foreach (ODataItemBase childItem in resourceInfoWrapper.NestedItems)
338+
IList<ODataItemBase> nestedItems;
339+
ODataEntityReferenceLinkBase[] referenceLinks = resourceInfoWrapper.NestedItems.OfType<ODataEntityReferenceLinkBase>().ToArray();
340+
if (referenceLinks.Length > 0)
341+
{
342+
// Be noted:
343+
// 1) OData v4.0, it's "Orders@odata.bind", and we get "ODataEntityReferenceLinkWrapper"(s) for that.
344+
// 2) OData v4.01, it's {"odata.id" ...}, and we get "ODataResource"(s) for that.
345+
// So, in OData v4, if it's a single, NestedItems contains one ODataEntityReferenceLinkWrapper,
346+
// if it's a collection, NestedItems contains multiple ODataEntityReferenceLinkWrapper(s)
347+
// We can use the following code to adjust the `ODataEntityReferenceLinkWrapper` to `ODataResourceWrapper`.
348+
// In OData v4.01, we will not be here.
349+
// Only supports declared property
350+
Contract.Assert(edmProperty != null);
351+
352+
nestedItems = new List<ODataItemBase>();
353+
if (edmProperty.Type.IsCollection())
354+
{
355+
IEdmCollectionTypeReference edmCollectionTypeReference = edmProperty.Type.AsCollection();
356+
ODataResourceSetWrapper resourceSetWrapper = CreateResourceSetWrapper(edmCollectionTypeReference, referenceLinks, readContext);
357+
nestedItems.Add(resourceSetWrapper);
358+
}
359+
else
360+
{
361+
ODataResourceWrapper resourceWrapper = CreateResourceWrapper(edmProperty.Type, referenceLinks[0], readContext);
362+
nestedItems.Add(resourceWrapper);
363+
}
364+
}
365+
else
366+
{
367+
nestedItems = resourceInfoWrapper.NestedItems;
368+
}
369+
370+
foreach (ODataItemBase childItem in nestedItems)
335371
{
336372
// it maybe null.
337373
if (childItem == null)
@@ -347,14 +383,7 @@ public virtual void ApplyNestedProperty(object resource, ODataNestedResourceInfo
347383
ApplyResourceInNestedProperty(edmProperty, resource, null, readContext);
348384
}
349385
}
350-
351-
ODataEntityReferenceLinkBase entityReferenceLink = childItem as ODataEntityReferenceLinkBase;
352-
if (entityReferenceLink != null)
353-
{
354-
// ignore entity reference links.
355-
continue;
356-
}
357-
386+
358387
ODataResourceSetWrapperBase resourceSetWrapper = childItem as ODataResourceSetWrapperBase;
359388
if (resourceSetWrapper != null)
360389
{
@@ -388,6 +417,137 @@ public virtual void ApplyNestedProperty(object resource, ODataNestedResourceInfo
388417
}
389418
}
390419

420+
421+
private ODataResourceSetWrapper CreateResourceSetWrapper(IEdmCollectionTypeReference edmPropertyType,
422+
IList<ODataEntityReferenceLinkBase> refLinks, ODataDeserializerContext readContext)
423+
{
424+
ODataResourceSet resourceSet = new ODataResourceSet
425+
{
426+
TypeName = edmPropertyType.FullName(),
427+
};
428+
429+
IEdmTypeReference elementType = edmPropertyType.ElementType();
430+
ODataResourceSetWrapper resourceSetWrapper = new ODataResourceSetWrapper(resourceSet);
431+
foreach (ODataEntityReferenceLinkBase refLinkWrapper in refLinks)
432+
{
433+
ODataResourceWrapper resourceWrapper = CreateResourceWrapper(elementType, refLinkWrapper, readContext);
434+
resourceSetWrapper.Resources.Add(resourceWrapper);
435+
}
436+
437+
return resourceSetWrapper;
438+
}
439+
440+
private ODataResourceWrapper CreateResourceWrapper(IEdmTypeReference edmPropertyType, ODataEntityReferenceLinkBase refLink, ODataDeserializerContext readContext)
441+
{
442+
Contract.Assert(readContext != null);
443+
444+
ODataResource resource = new ODataResource
445+
{
446+
TypeName = edmPropertyType.FullName(),
447+
};
448+
449+
resource.Properties = CreateKeyProperties(refLink.EntityReferenceLink.Url, readContext);
450+
451+
if (refLink.EntityReferenceLink.InstanceAnnotations != null)
452+
{
453+
foreach (ODataInstanceAnnotation instanceAnnotation in refLink.EntityReferenceLink.InstanceAnnotations)
454+
{
455+
resource.InstanceAnnotations.Add(instanceAnnotation);
456+
};
457+
}
458+
459+
return new ODataResourceWrapper(resource);
460+
}
461+
462+
/// <summary>
463+
/// Update the resource wrapper if it has the "Id" value.
464+
/// </summary>
465+
/// <param name="resourceWrapper">The resource wrapper.</param>
466+
/// <param name="readContext">The read context.</param>
467+
/// <returns>The resource wrapper.</returns>
468+
private ODataResourceWrapper UpdateResourceWrapper(ODataResourceWrapper resourceWrapper, ODataDeserializerContext readContext)
469+
{
470+
Contract.Assert(readContext != null);
471+
472+
if (resourceWrapper?.Resource?.Id == null)
473+
{
474+
return resourceWrapper;
475+
}
476+
477+
IEnumerable<ODataProperty> keys = CreateKeyProperties(resourceWrapper.Resource.Id, readContext);
478+
if (keys == null)
479+
{
480+
return resourceWrapper;
481+
}
482+
483+
if (resourceWrapper.Resource.Properties == null)
484+
{
485+
resourceWrapper.Resource.Properties = keys;
486+
}
487+
else
488+
{
489+
IDictionary<string, ODataProperty> newPropertiesDic = resourceWrapper.Resource.Properties.ToDictionary(p => p.Name, p => p);
490+
foreach (ODataProperty key in keys)
491+
{
492+
if (!newPropertiesDic.ContainsKey(key.Name))
493+
{
494+
newPropertiesDic[key.Name] = key;
495+
}
496+
}
497+
498+
resourceWrapper.Resource.Properties = newPropertiesDic.Values;
499+
}
500+
501+
return resourceWrapper;
502+
}
503+
504+
/// <summary>
505+
/// Do uri parsing to get the key values.
506+
/// </summary>
507+
/// <param name="id">The key Id.</param>
508+
/// <param name="readContext">The reader context.</param>
509+
/// <returns>The key properties.</returns>
510+
private static IList<ODataProperty> CreateKeyProperties(Uri id, ODataDeserializerContext readContext)
511+
{
512+
Contract.Assert(id != null);
513+
Contract.Assert(readContext != null);
514+
IList<ODataProperty> properties = new List<ODataProperty>();
515+
if (readContext.Request == null)
516+
{
517+
return properties;
518+
}
519+
520+
IODataPathHandler pathHandler = readContext.InternalRequest.PathHandler;
521+
IWebApiRequestMessage internalRequest = readContext.InternalRequest;
522+
IWebApiUrlHelper urlHelper = readContext.InternalUrlHelper;
523+
try
524+
{
525+
string serviceRoot = urlHelper.CreateODataLink(
526+
internalRequest.Context.RouteName,
527+
internalRequest.PathHandler,
528+
new List<ODataPathSegment>());
529+
ODataPath odataPath = pathHandler.Parse(serviceRoot, id.OriginalString, internalRequest.RequestContainer);
530+
KeySegment keySegment = odataPath.Segments.OfType<KeySegment>().LastOrDefault();
531+
if (keySegment != null)
532+
{
533+
foreach (KeyValuePair<string, object> key in keySegment.Keys)
534+
{
535+
properties.Add(new ODataProperty
536+
{
537+
Name = key.Key,
538+
Value = key.Value
539+
});
540+
}
541+
}
542+
543+
return properties;
544+
}
545+
catch (Exception)
546+
{
547+
return properties;
548+
}
549+
}
550+
391551
/// <summary>
392552
/// Deserializes the structural properties from <paramref name="resourceWrapper"/> into <paramref name="resource"/>.
393553
/// </summary>
@@ -516,7 +676,7 @@ private object ReadNestedResourceInline(ODataResourceWrapper resourceWrapper, IE
516676
{
517677
Path = readContext.Path,
518678
Model = readContext.Model,
519-
Request = readContext.Request,
679+
Request = readContext.Request
520680
};
521681

522682
Type clrType = null;
@@ -542,7 +702,6 @@ private object ReadNestedResourceInline(ODataResourceWrapper resourceWrapper, IE
542702
: clrType;
543703
return deserializer.ReadInline(resourceWrapper, edmType, nestedReadContext);
544704
}
545-
546705
private void ApplyResourceSetInNestedProperty(IEdmProperty nestedProperty, object resource,
547706
ODataResourceSetWrapperBase resourceSetWrapper, ODataDeserializerContext readContext)
548707
{
@@ -614,7 +773,7 @@ private object ReadNestedResourceSetInline(ODataResourceSetWrapperBase resourceS
614773
{
615774
Path = readContext.Path,
616775
Model = readContext.Model,
617-
Request = readContext.Request,
776+
Request = readContext.Request
618777
};
619778

620779
if (readContext.IsUntyped)

0 commit comments

Comments
 (0)