/// <summary> /// Reads a property value starting on a complex value. /// </summary> /// <param name="complexValue">The complex value to start with.</param> /// <param name="complexPropertySegment">The EPM source path segment which points to the <paramref name="complexValue"/>.</param> /// <param name="epmValueCache">The EPM value cache to use.</param> /// <param name="sourceSegmentIndex">The index in the property value path to start with.</param> /// <param name="resourceType">The resource type of the complex value.</param> /// <param name="metadata">The metadata provider to use.</param> /// <param name="nullOnParentProperty">true if the value of the property is null because one of its parent properties was null, in this case /// the return value of the method is always null. false if the value of the property is the actual property value which may or may not be null.</param> /// <returns>The value of the property (may be null), or null if the property itself was not found due to one of its parent properties being null.</returns> private object ReadComplexPropertyValue( ODataComplexValue complexValue, EpmSourcePathSegment complexPropertySegment, EpmValueCache epmValueCache, int sourceSegmentIndex, ResourceType resourceType, DataServiceMetadataProviderWrapper metadata, out bool nullOnParentProperty) { Debug.Assert(this.propertyValuePath != null, "The propertyValuePath should have been initialized by now."); Debug.Assert(this.propertyValuePath.Length > sourceSegmentIndex, "The propertyValuePath must be at least as long as the source segment index."); Debug.Assert(epmValueCache != null, "epmValueCache != null"); Debug.Assert(sourceSegmentIndex >= 0, "sourceSegmentIndex >= 0"); Debug.Assert(resourceType != null, "resourceType != null"); if (complexValue == null) { nullOnParentProperty = true; return(null); } return(this.ReadPropertyValue( EpmValueCache.GetComplexValueProperties(epmValueCache, complexPropertySegment, complexValue, false), sourceSegmentIndex, resourceType, metadata, epmValueCache, out nullOnParentProperty)); }
/// <summary> /// Writes out the value of a complex property. /// </summary> /// <param name="writer">The <see cref="XmlWriter"/> to write to.</param> /// <param name="metadata">The metadata provider to use or null if no metadata is available.</param> /// <param name="complexValue">The complex value to write.</param> /// <param name="metadataType">The metadata type for the complex value.</param> /// <param name="isOpenPropertyType">True if the type name belongs to an open property.</param> /// <param name="isWritingCollection">True if we are writing a collection instead of an entry.</param> /// <param name="version">The protocol version used for writing.</param> /// <param name="epmValueCache">Cache of values used in EPM so that we avoid multiple enumerations of properties/items. (can be null)</param> /// <param name="epmSourcePathSegment">The EPM source path segment which points to the property we're writing. (can be null)</param> internal static void WriteComplexValue( XmlWriter writer, DataServiceMetadataProviderWrapper metadata, ODataComplexValue complexValue, ResourceType metadataType, bool isOpenPropertyType, bool isWritingCollection, ODataVersion version, EpmValueCache epmValueCache, EpmSourcePathSegment epmSourcePathSegment) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(complexValue != null, "complexValue != null"); string typeName = complexValue.TypeName; // resolve the type name to the resource type; if no type name is specified we will use the // type inferred from metadata ResourceType complexValueType = MetadataUtils.ResolveTypeName(metadata, metadataType, ref typeName, ResourceTypeKind.ComplexType, isOpenPropertyType); if (typeName != null) { WritePropertyTypeAttribute(writer, typeName); } WriteProperties( writer, metadata, complexValueType, EpmValueCache.GetComplexValueProperties(epmValueCache, epmSourcePathSegment, complexValue, true), version, isWritingCollection, epmValueCache, epmSourcePathSegment); }
/// <summary> /// Returns the items for the specified multivalue. /// </summary> /// <param name="epmValueCache">The EPM value cache to use (can be null).</param> /// <param name="sourcePathSegment">The source path segment for the property which has this multivalue.</param> /// <param name="multiValue">The multivalue to get the items for.</param> /// <param name="writingContent">If we're writing content of an entry or not.</param> /// <returns>The items enumeration for the multivalue.</returns> internal static IEnumerable GetMultiValueItems( EpmValueCache epmValueCache, EpmSourcePathSegment sourcePathSegment, ODataMultiValue multiValue, bool writingContent) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(multiValue != null, "multiValue != null"); Debug.Assert(writingContent || epmValueCache != null, "If we're not writing content, then the EPM value cache must exist."); if (epmValueCache == null) { return(multiValue.Items); } else { return(epmValueCache.GetMultiValueItems(sourcePathSegment, multiValue, writingContent)); } }
/// <summary> /// Returns the properties for the specified complex value. /// </summary> /// <param name="epmValueCache">The EPM value cache to use (can be null).</param> /// <param name="sourcePathSegment">The source path segment for the property which has this complex value.</param> /// <param name="complexValue">The complex value to get the properties for.</param> /// <param name="writingContent">If we're writing content of an entry or not.</param> /// <returns>The properties enumeration for the complex value.</returns> internal static IEnumerable <ODataProperty> GetComplexValueProperties( EpmValueCache epmValueCache, EpmSourcePathSegment sourcePathSegment, ODataComplexValue complexValue, bool writingContent) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(complexValue != null, "complexValue != null"); Debug.Assert(writingContent || epmValueCache != null, "If we're not writing content, then the EPM value cache must exist."); if (epmValueCache == null) { return(complexValue.Properties); } else { return(epmValueCache.GetComplexValueProperties(sourcePathSegment, complexValue, writingContent)); } }
/// <summary> /// Returns the items for the specified multivalue. /// </summary> /// <param name="epmValueCache">The EPM value cache to use (can be null).</param> /// <param name="sourcePathSegment">The source path segment for the property which has this multivalue.</param> /// <param name="multiValue">The multivalue to get the items for.</param> /// <param name="writingContent">If we're writing content of an entry or not.</param> /// <returns>The items enumeration for the multivalue.</returns> internal static IEnumerable GetMultiValueItems( EpmValueCache epmValueCache, EpmSourcePathSegment sourcePathSegment, ODataMultiValue multiValue, bool writingContent) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(multiValue != null, "multiValue != null"); Debug.Assert(writingContent || epmValueCache != null, "If we're not writing content, then the EPM value cache must exist."); if (epmValueCache == null) { return multiValue.Items; } else { return epmValueCache.GetMultiValueItems(sourcePathSegment, multiValue, writingContent); } }
/// <summary> /// Returns the properties for the specified complex value. /// </summary> /// <param name="epmValueCache">The EPM value cache to use (can be null).</param> /// <param name="sourcePathSegment">The source path segment for the property which has this complex value.</param> /// <param name="complexValue">The complex value to get the properties for.</param> /// <param name="writingContent">If we're writing content of an entry or not.</param> /// <returns>The properties enumeration for the complex value.</returns> internal static IEnumerable<ODataProperty> GetComplexValueProperties( EpmValueCache epmValueCache, EpmSourcePathSegment sourcePathSegment, ODataComplexValue complexValue, bool writingContent) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(complexValue != null, "complexValue != null"); Debug.Assert(writingContent || epmValueCache != null, "If we're not writing content, then the EPM value cache must exist."); if (epmValueCache == null) { return complexValue.Properties; } else { return epmValueCache.GetComplexValueProperties(sourcePathSegment, complexValue, writingContent); } }
/// <summary> /// Write the given collection of properties. /// </summary> /// <param name="writer">The <see cref="XmlWriter"/> to write to.</param> /// <param name="metadata">The metadata provider to use or null if no metadata is available.</param> /// <param name="owningType">The <see cref="ResourceType"/> of the entry (or null if not metadata is available).</param> /// <param name="cachedProperties">Collection of cached properties for the entry.</param> /// <param name="version">The protocol version used for writing.</param> /// <param name="isWritingCollection">True if we are writing a collection instead of an entry.</param> /// <param name="epmValueCache">Cache of values used in EPM so that we avoid multiple enumerations of properties/items. (can be null)</param> /// <param name="epmSourcePathSegment">The EPM source path segment which points to the property which sub-properites we're writing. (can be null)</param> internal static void WriteProperties( XmlWriter writer, DataServiceMetadataProviderWrapper metadata, ResourceType owningType, IEnumerable <ODataProperty> cachedProperties, ODataVersion version, bool isWritingCollection, EpmValueCache epmValueCache, EpmSourcePathSegment epmSourcePathSegment) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(writer != null, "writer != null"); if (cachedProperties == null) { return; } foreach (ODataProperty property in cachedProperties) { WriteProperty(writer, metadata, property, owningType, version, false, isWritingCollection, epmValueCache, epmSourcePathSegment); } }
/// <summary> /// Write the items in a MultiValue in ATOM format. /// </summary> /// <param name="writer">The <see cref="XmlWriter"/> to write to.</param> /// <param name="metadata">The metadata provider to use or null if no metadata is available.</param> /// <param name="multiValue">The MultiValue to write.</param> /// <param name="resourcePropertyType">The resource type of the multi value (or null if not metadata is available).</param> /// <param name="isOpenPropertyType">True if the type name belongs to an open property.</param> /// <param name="isWritingCollection">True if we are writing a collection instead of an entry.</param> /// <param name="version">The protocol version used for writing.</param> /// <param name="epmValueCache">Cache of values used in EPM so that we avoid multiple enumerations of properties/items. (can be null)</param> /// <param name="epmSourcePathSegment">The EPM source path segment which points to the multivalue property we're writing. (can be null)</param> private static void WriteMultiValue( XmlWriter writer, DataServiceMetadataProviderWrapper metadata, ODataMultiValue multiValue, ResourceType resourcePropertyType, bool isOpenPropertyType, bool isWritingCollection, ODataVersion version, EpmValueCache epmValueCache, EpmSourcePathSegment epmSourcePathSegment) { Debug.Assert(multiValue != null, "multiValue != null"); string typeName = multiValue.TypeName; // resolve the type name to the resource type; if no type name is specified we will use the // type inferred from metadata MultiValueResourceType multiValueType = (MultiValueResourceType)MetadataUtils.ResolveTypeName(metadata, resourcePropertyType, ref typeName, ResourceTypeKind.MultiValue, isOpenPropertyType); if (typeName != null) { WritePropertyTypeAttribute(writer, typeName); } ResourceType expectedItemType = multiValueType == null ? null : multiValueType.ItemType; IEnumerable items = EpmValueCache.GetMultiValueItems(epmValueCache, epmSourcePathSegment, multiValue, true); if (items != null) { foreach (object itemValue in items) { object item; EpmMultiValueItemCache epmItemCache = itemValue as EpmMultiValueItemCache; if (epmItemCache != null) { item = epmItemCache.ItemValue; } else { item = itemValue; } ValidationUtils.ValidateMultiValueItem(item); writer.WriteStartElement(AtomConstants.ODataNamespacePrefix, AtomConstants.ODataMultiValueItemElementName, AtomConstants.ODataNamespace); ODataComplexValue complexValue = item as ODataComplexValue; if (complexValue != null) { WriteComplexValue(writer, metadata, complexValue, expectedItemType, false, isWritingCollection, version, epmItemCache, epmSourcePathSegment); } else { ODataMultiValue multiValueItem = item as ODataMultiValue; if (multiValueItem != null) { throw new ODataException(Strings.ODataWriter_NestedMultiValuesAreNotSupported); } else { AtomValueUtils.WritePrimitiveValue(writer, item, expectedItemType); } } writer.WriteEndElement(); } } }
/// <summary> /// Writes a single property in ATOM format. /// </summary> /// <param name="writer">The <see cref="XmlWriter"/> to write to.</param> /// <param name="metadata">The metadata provider to use or null if no metadata is available.</param> /// <param name="property">The property to write out.</param> /// <param name="owningType">The type owning the property (or null if no metadata is available).</param> /// <param name="version">The protocol version used for writing.</param> /// <param name="isTopLevel">True if writing a top-level property payload; otherwise false.</param> /// <param name="isWritingCollection">True if we are writing a collection instead of an entry.</param> /// <param name="epmValueCache">Cache of values used in EPM so that we avoid multiple enumerations of properties/items. (can be null)</param> /// <param name="epmParentSourcePathSegment">The EPM source path segment which points to the property which sub-property we're writing. (can be null)</param> internal static void WriteProperty( XmlWriter writer, DataServiceMetadataProviderWrapper metadata, ODataProperty property, ResourceType owningType, ODataVersion version, bool isTopLevel, bool isWritingCollection, EpmValueCache epmValueCache, EpmSourcePathSegment epmParentSourcePathSegment) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(writer != null, "writer != null"); ValidationUtils.ValidateProperty(property); ResourceProperty resourceProperty = ValidationUtils.ValidatePropertyDefined(property.Name, owningType); EpmSourcePathSegment epmSourcePathSegment = null; if (epmParentSourcePathSegment != null) { epmSourcePathSegment = epmParentSourcePathSegment.SubProperties.Where(subProperty => subProperty.PropertyName == property.Name).FirstOrDefault(); } object value = property.Value; // TODO: If we implement validation or type conversions the value needs to be converted here // since the next method call needs to know if the value is a string or not in some cases. // If EPM tells us to skip this property in content, then we're done here. if (!ShouldWritePropertyInContent(value, epmSourcePathSegment, version)) { return; } // <d:propertyname> writer.WriteStartElement( isWritingCollection ? string.Empty : AtomConstants.ODataNamespacePrefix, property.Name, AtomConstants.ODataNamespace); if (isTopLevel) { WriteDefaultNamespaceAttributes(writer, DefaultNamespaceFlags.OData | DefaultNamespaceFlags.ODataMetadata); } // Null property value. if (value == null) { // verify that MultiValue properties are not null if (resourceProperty != null && resourceProperty.Kind == ResourcePropertyKind.MultiValue) { throw new ODataException(Strings.ODataWriter_MultiValuePropertiesMustNotHaveNullValue(resourceProperty.Name)); } ODataAtomWriterUtils.WriteNullAttribute(writer); } else { ODataComplexValue complexValue = value as ODataComplexValue; ResourceType resourcePropertyType = resourceProperty == null ? null : resourceProperty.ResourceType; bool isOpenPropertyType = owningType != null && owningType.IsOpenType && resourceProperty == null; // Complex properties are written recursively. if (complexValue != null) { WriteComplexValue(writer, metadata, complexValue, resourcePropertyType, isOpenPropertyType, isWritingCollection, version, epmValueCache, epmSourcePathSegment); } else { ODataMultiValue multiValue = value as ODataMultiValue; if (multiValue != null) { ODataVersionChecker.CheckMultiValueProperties(version, property.Name); WriteMultiValue(writer, metadata, multiValue, resourcePropertyType, isOpenPropertyType, isWritingCollection, version, epmValueCache, epmSourcePathSegment); } else { WritePrimitiveValue(writer, value, resourcePropertyType); } } } // </d:propertyname> writer.WriteEndElement(); }
/// <summary> /// Write the given collection of properties. /// </summary> /// <param name="writer">The <see cref="XmlWriter"/> to write to.</param> /// <param name="metadata">The metadata provider to use or null if no metadata is available.</param> /// <param name="owningType">The <see cref="ResourceType"/> of the entry (or null if not metadata is available).</param> /// <param name="cachedProperties">Collection of cached properties for the entry.</param> /// <param name="version">The protocol version used for writing.</param> /// <param name="isWritingCollection">True if we are writing a collection instead of an entry.</param> /// <param name="epmValueCache">Cache of values used in EPM so that we avoid multiple enumerations of properties/items. (can be null)</param> /// <param name="epmSourcePathSegment">The EPM source path segment which points to the property which sub-properites we're writing. (can be null)</param> internal static void WriteProperties( XmlWriter writer, DataServiceMetadataProviderWrapper metadata, ResourceType owningType, IEnumerable<ODataProperty> cachedProperties, ODataVersion version, bool isWritingCollection, EpmValueCache epmValueCache, EpmSourcePathSegment epmSourcePathSegment) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(writer != null, "writer != null"); if (cachedProperties == null) { return; } foreach (ODataProperty property in cachedProperties) { WriteProperty(writer, metadata, property, owningType, version, false, isWritingCollection, epmValueCache, epmSourcePathSegment); } }
/// <summary> /// Reads a property value starting with the specified index to the property value path. /// </summary> /// <param name="cachedProperties">The enumeration of properties to search for the first property in the property value path.</param> /// <param name="sourceSegmentIndex">The index in the property value path to start with.</param> /// <param name="resourceType">The resource type of the entry or complex value the <paramref name="cachedProperties"/> enumeration belongs to.</param> /// <param name="metadata">The metadata provider to use.</param> /// <param name="epmValueCache">The EPM value cache to use.</param> /// <param name="nullOnParentProperty">true if the value of the property is null because one of its parent properties was null, in this case /// the return value of the method is always null. false if the value of the property is the actual property value which may or may not be null.</param> /// <returns>The value of the property (may be null), or null if the property itself was not found due to one of its parent properties being null.</returns> private object ReadPropertyValue( IEnumerable<ODataProperty> cachedProperties, int sourceSegmentIndex, ResourceType resourceType, DataServiceMetadataProviderWrapper metadata, EpmValueCache epmValueCache, out bool nullOnParentProperty) { Debug.Assert(this.propertyValuePath != null, "The propertyValuePath should have been initialized by now."); Debug.Assert(this.propertyValuePath.Length > sourceSegmentIndex, "The propertyValuePath must be at least as long as the source segment index."); Debug.Assert(resourceType != null, "resourceType != null"); Debug.Assert(epmValueCache != null, "epmValueCache != null"); EpmSourcePathSegment sourceSegment = this.propertyValuePath[sourceSegmentIndex]; string propertyName = sourceSegment.PropertyName; bool lastSegment = this.propertyValuePath.Length == sourceSegmentIndex + 1; ResourceProperty resourceProperty = ValidationUtils.ValidatePropertyDefined(propertyName, resourceType); if (resourceProperty != null) { // If this is the last part of the path, then it has to be a primitive or multiValue type otherwise should be a complex type if (lastSegment) { if (!resourceProperty.IsOfKind(ResourcePropertyKind.Primitive) && !resourceProperty.IsOfKind(ResourcePropertyKind.MultiValue)) { throw new ODataException(Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName)); } } else { if (!resourceProperty.IsOfKind(ResourcePropertyKind.ComplexType)) { throw new ODataException(Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName)); } } } else { Debug.Assert( resourceType.IsOpenType, "Only open types can have undeclared properties, otherwise we should have failed in the ValidatePropertyDefined method."); } ODataProperty property = null; if (cachedProperties != null) { property = cachedProperties.FirstOrDefault(p => p.Name == propertyName); } if (property == null) { throw new ODataException(Strings.EpmSourceTree_MissingPropertyOnInstance(propertyName, resourceType.FullName)); } object propertyValue = property.Value; ODataComplexValue propertyComplexValue = propertyValue as ODataComplexValue; if (lastSegment) { if (propertyValue != null && resourceProperty != null) { ValidationUtils.ValidateIsExpectedPrimitiveType(propertyValue, resourceProperty.ResourceType); } // If this property is the last one it has to be either a primitive or multivalue // TODO: Check for named streams here as well if (propertyComplexValue != null) { throw new ODataException(Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName)); } nullOnParentProperty = false; return propertyValue; } else { // Otherwise it's in the middle and thus it must be a complex value if (propertyComplexValue == null) { throw new ODataException(Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName)); } string typeName = propertyComplexValue.TypeName; ResourceType complexValueType = MetadataUtils.ResolveTypeName( metadata, resourceProperty == null ? null : resourceProperty.ResourceType, ref typeName, ResourceTypeKind.ComplexType, resourceProperty == null); return this.ReadComplexPropertyValue( propertyComplexValue, sourceSegment, epmValueCache, sourceSegmentIndex + 1, complexValueType, metadata, out nullOnParentProperty); } }
/// <summary> /// Reads a property value starting on a complex value. /// </summary> /// <param name="complexValue">The complex value to start with.</param> /// <param name="complexPropertySegment">The EPM source path segment which points to the <paramref name="complexValue"/>.</param> /// <param name="epmValueCache">The EPM value cache to use.</param> /// <param name="sourceSegmentIndex">The index in the property value path to start with.</param> /// <param name="resourceType">The resource type of the complex value.</param> /// <param name="metadata">The metadata provider to use.</param> /// <param name="nullOnParentProperty">true if the value of the property is null because one of its parent properties was null, in this case /// the return value of the method is always null. false if the value of the property is the actual property value which may or may not be null.</param> /// <returns>The value of the property (may be null), or null if the property itself was not found due to one of its parent properties being null.</returns> private object ReadComplexPropertyValue( ODataComplexValue complexValue, EpmSourcePathSegment complexPropertySegment, EpmValueCache epmValueCache, int sourceSegmentIndex, ResourceType resourceType, DataServiceMetadataProviderWrapper metadata, out bool nullOnParentProperty) { Debug.Assert(this.propertyValuePath != null, "The propertyValuePath should have been initialized by now."); Debug.Assert(this.propertyValuePath.Length > sourceSegmentIndex, "The propertyValuePath must be at least as long as the source segment index."); Debug.Assert(epmValueCache != null, "epmValueCache != null"); Debug.Assert(sourceSegmentIndex >= 0, "sourceSegmentIndex >= 0"); Debug.Assert(resourceType != null, "resourceType != null"); if (complexValue == null) { nullOnParentProperty = true; return null; } return this.ReadPropertyValue( EpmValueCache.GetComplexValueProperties(epmValueCache, complexPropertySegment, complexValue, false), sourceSegmentIndex, resourceType, metadata, epmValueCache, out nullOnParentProperty); }
/// <summary> /// Reads a property value starting with the specified index to the property value path. /// </summary> /// <param name="cachedProperties">The enumeration of properties to search for the first property in the property value path.</param> /// <param name="sourceSegmentIndex">The index in the property value path to start with.</param> /// <param name="resourceType">The resource type of the entry or complex value the <paramref name="cachedProperties"/> enumeration belongs to.</param> /// <param name="metadata">The metadata provider to use.</param> /// <param name="epmValueCache">The EPM value cache to use.</param> /// <param name="nullOnParentProperty">true if the value of the property is null because one of its parent properties was null, in this case /// the return value of the method is always null. false if the value of the property is the actual property value which may or may not be null.</param> /// <returns>The value of the property (may be null), or null if the property itself was not found due to one of its parent properties being null.</returns> private object ReadPropertyValue( IEnumerable <ODataProperty> cachedProperties, int sourceSegmentIndex, ResourceType resourceType, DataServiceMetadataProviderWrapper metadata, EpmValueCache epmValueCache, out bool nullOnParentProperty) { Debug.Assert(this.propertyValuePath != null, "The propertyValuePath should have been initialized by now."); Debug.Assert(this.propertyValuePath.Length > sourceSegmentIndex, "The propertyValuePath must be at least as long as the source segment index."); Debug.Assert(resourceType != null, "resourceType != null"); Debug.Assert(epmValueCache != null, "epmValueCache != null"); EpmSourcePathSegment sourceSegment = this.propertyValuePath[sourceSegmentIndex]; string propertyName = sourceSegment.PropertyName; bool lastSegment = this.propertyValuePath.Length == sourceSegmentIndex + 1; ResourceProperty resourceProperty = ValidationUtils.ValidatePropertyDefined(propertyName, resourceType); if (resourceProperty != null) { // If this is the last part of the path, then it has to be a primitive or multiValue type otherwise should be a complex type if (lastSegment) { if (!resourceProperty.IsOfKind(ResourcePropertyKind.Primitive) && !resourceProperty.IsOfKind(ResourcePropertyKind.MultiValue)) { throw new ODataException(Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName)); } } else { if (!resourceProperty.IsOfKind(ResourcePropertyKind.ComplexType)) { throw new ODataException(Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName)); } } } else { Debug.Assert( resourceType.IsOpenType, "Only open types can have undeclared properties, otherwise we should have failed in the ValidatePropertyDefined method."); } ODataProperty property = null; if (cachedProperties != null) { property = cachedProperties.FirstOrDefault(p => p.Name == propertyName); } if (property == null) { throw new ODataException(Strings.EpmSourceTree_MissingPropertyOnInstance(propertyName, resourceType.FullName)); } object propertyValue = property.Value; ODataComplexValue propertyComplexValue = propertyValue as ODataComplexValue; if (lastSegment) { if (propertyValue != null && resourceProperty != null) { ValidationUtils.ValidateIsExpectedPrimitiveType(propertyValue, resourceProperty.ResourceType); } // If this property is the last one it has to be either a primitive or multivalue // TODO: Check for named streams here as well if (propertyComplexValue != null) { throw new ODataException(Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName)); } nullOnParentProperty = false; return(propertyValue); } else { // Otherwise it's in the middle and thus it must be a complex value if (propertyComplexValue == null) { throw new ODataException(Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName)); } string typeName = propertyComplexValue.TypeName; ResourceType complexValueType = MetadataUtils.ResolveTypeName( metadata, resourceProperty == null ? null : resourceProperty.ResourceType, ref typeName, ResourceTypeKind.ComplexType, resourceProperty == null); return(this.ReadComplexPropertyValue( propertyComplexValue, sourceSegment, epmValueCache, sourceSegmentIndex + 1, complexValueType, metadata, out nullOnParentProperty)); } }