/// <summary>Materializes the specified <paramref name="entry"/> as dynamic property.</summary> /// <param name="entry">Entry with object to materialize.</param> /// <param name="link">Nested resource link as parsed.</param> private void MaterializeDynamicProperty(MaterializerEntry entry, ODataNestedResourceInfo link) { Debug.Assert(entry != null, "entry != null"); Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise not resolved/created!"); Debug.Assert(link != null, "link != null"); ClientEdmModel model = this.MaterializerContext.Model; IDictionary <string, object> containerProperty; // Stop if owning type is not an open type // Or container property is not found // Or key with matching name already exists in the dictionary if (!ClientTypeUtil.IsInstanceOfOpenType(entry.ResolvedObject, model) || !ClientTypeUtil.TryGetContainerProperty(entry.ResolvedObject, out containerProperty) || containerProperty.ContainsKey(link.Name)) { return; } MaterializerNavigationLink linkState = MaterializerNavigationLink.GetLink(link); if (linkState == null || (linkState.Entry == null && linkState.Feed == null)) { return; } // NOTE: ODL (and OData WebApi) support navigational property on complex types // That support has not yet been implemented in OData client // An entity or entity collection as a dynamic property currently doesn't work as expected // due to the absence of a navigational property definition in the metadata // to express the relationship between that entity and the parent entity - unless they're the same type! // Only materialize a nested resource if its a complex or complex collection if (linkState.Feed != null) { string collectionTypeName = linkState.Feed.TypeName; // TypeName represents a collection e.g. Collection(NS.Type) string collectionItemTypeName = CommonUtil.GetCollectionItemTypeName(collectionTypeName, false); // Highly unlikely, but the method return null if the typeName argument does not meet certain expectations if (collectionItemTypeName == null) { return; } Type collectionItemType = ResolveClientTypeForDynamicProperty(collectionItemTypeName, entry.ResolvedObject); if (collectionItemType != null && ClientTypeUtil.TypeIsComplex(collectionItemType, model)) { Type collectionType = typeof(System.Collections.ObjectModel.Collection <>).MakeGenericType(new Type[] { collectionItemType }); IList collection = (IList)Util.ActivatorCreateInstance(collectionType); IEnumerable <ODataResource> feedEntries = MaterializerFeed.GetFeed(linkState.Feed).Entries; foreach (ODataResource feedEntry in feedEntries) { MaterializerEntry linkEntry = MaterializerEntry.GetEntry(feedEntry); this.Materialize(linkEntry, collectionItemType, false /*includeLinks*/); collection.Add(linkEntry.ResolvedObject); } containerProperty.Add(link.Name, collection); } } else { MaterializerEntry linkEntry = linkState.Entry; Type itemType = ResolveClientTypeForDynamicProperty(linkEntry.Entry.TypeName, entry.ResolvedObject); if (itemType != null && ClientTypeUtil.TypeIsComplex(itemType, model)) { this.Materialize(linkEntry, itemType, false /*includeLinks*/); containerProperty.Add(link.Name, linkEntry.ResolvedObject); } } }
/// <summary> /// Adds a data value to the dynamic properties dictionary (where it exists) on the specified <paramref name="instance"/> /// </summary> /// <param name="property">Property containing unmaterialzed value to apply</param> /// <param name="instance">Instance that may optionally contain the dynamic properties dictionary</param> internal void MaterializeDynamicProperty(ODataProperty property, object instance) { Debug.Assert(property != null, "property != null"); Debug.Assert(instance != null, "instance != null"); IDictionary <string, object> containerProperty; // Stop if owning type is not an open type // Or container property is not found // Or key with matching name already exists in the dictionary if (!ClientTypeUtil.IsInstanceOfOpenType(instance, this.MaterializerContext.Model) || !ClientTypeUtil.TryGetContainerProperty(instance, out containerProperty) || containerProperty.ContainsKey(property.Name)) { return; } object value = property.Value; // Handles properties of known types returned with type annotations if (!(value is ODataValue) && PrimitiveType.IsKnownType(value.GetType())) { containerProperty.Add(property.Name, value); return; } // Handle untyped value ODataUntypedValue untypedVal = value as ODataUntypedValue; if (untypedVal != null) { value = CommonUtil.ParseJsonToPrimitiveValue(untypedVal.RawValue); containerProperty.Add(property.Name, value); return; } // Handle enum value ODataEnumValue enumVal = value as ODataEnumValue; if (enumVal != null) { Type clientType = ResolveClientTypeForDynamicProperty(enumVal.TypeName, instance); // Unable to resolve type for dynamic property if (clientType == null) { return; } object materializedEnumValue; if (EnumValueMaterializationPolicy.TryMaterializeODataEnumValue(clientType, enumVal, out materializedEnumValue)) { containerProperty.Add(property.Name, materializedEnumValue); } return; } // Handle collection ODataCollectionValue collectionVal = value as ODataCollectionValue; if (collectionVal != null) { string collectionItemTypeName = CommonUtil.GetCollectionItemTypeName(collectionVal.TypeName, false); // Highly unlikely, but the method return null if the typeName argument does not meet certain expectations if (collectionItemTypeName == null) { return; } Type collectionItemType; // ToNamedType will return true for primitive types if (!ClientConvert.ToNamedType(collectionItemTypeName, out collectionItemType)) { // Non-primitive collection collectionItemType = ResolveClientTypeForDynamicProperty(collectionItemTypeName, instance); } if (collectionItemType == null) { return; } object collectionInstance; if (this.CollectionValueMaterializationPolicy.TryMaterializeODataCollectionValue(collectionItemType, property, out collectionInstance)) { containerProperty.Add(property.Name, collectionInstance); } return; } }