1111using System . Runtime . Serialization ;
1212using System . Threading . Tasks ;
1313using Microsoft . AspNet . OData . Common ;
14- using Microsoft . AspNet . OData . Formatter . Serialization ;
14+ using Microsoft . AspNet . OData . Interfaces ;
15+ using Microsoft . AspNet . OData . Routing ;
1516using Microsoft . OData ;
1617using Microsoft . OData . Edm ;
18+ using Microsoft . OData . UriParser ;
19+ using ODataPath = Microsoft . AspNet . OData . Routing . ODataPath ;
1720
1821namespace 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