/// <summary> /// Cache the property and collection item enumerations needed in EPM mappings. We do this for syndication and custom /// mappings. /// </summary> /// <param name="valueCache">The property value cache to use for caching.</param> /// <param name="segments">The source path segments to cache.</param> /// <param name="properties">The <see cref="ODataProperty"/> values to compute the segments against.</param> private static void CacheEpmSourcePathSegments(EpmValueCache valueCache, List <EpmSourcePathSegment> segments, IEnumerable <ODataProperty> properties) { Debug.Assert(valueCache != null, "valueCache != null"); Debug.Assert(segments != null, "segments != null"); if (properties == null) { return; } foreach (EpmSourcePathSegment segment in segments) { if (segment.EpmInfo == null) { // No EPM info means that this is a complex value (cannot be mapped directly); cache it. ODataComplexValue complexValue; if (TryGetPropertyValue <ODataComplexValue>(properties, segment.PropertyName, out complexValue)) { // Cache the properties of the complex property IEnumerable <ODataProperty> subProperties = valueCache.CacheComplexValueProperties(complexValue); CacheEpmSourcePathSegments(valueCache, segment.SubProperties, subProperties); } } else { // Primitive collection item inside a complex value; nothing to do - we cached the property values already. Debug.Assert(segment.SubProperties.Count == 0, "No sub-segments expected."); } } }
internal static IEnumerable<ODataProperty> GetComplexValueProperties(EpmValueCache epmValueCache, ODataComplexValue complexValue, bool writingContent) { if (epmValueCache == null) { return complexValue.Properties; } return epmValueCache.GetComplexValueProperties(complexValue, writingContent); }
internal static IEnumerable <ODataProperty> GetComplexValueProperties(EpmValueCache epmValueCache, ODataComplexValue complexValue, bool writingContent) { if (epmValueCache == null) { return(complexValue.Properties); } return(epmValueCache.GetComplexValueProperties(complexValue, writingContent)); }
/// <summary> /// Writes a property with a complex value in ATOM format. /// </summary> /// <param name="complexValue">The complex value to write.</param> /// <param name="propertyName">The name of the property being written.</param> /// <param name="isTopLevel">true if writing a top-level property payload; otherwise false.</param> /// <param name="isWritingCollection">true if we are writing a top-level collection instead of an entry.</param> /// <param name="beforeValueAction">Action called before the complex value is written, if it's actually written.</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="propertyTypeReference">The type information for the property being written.</param> /// <param name="isOpenPropertyType">true if the type name belongs to an open property.</param> /// <param name="epmSourcePathSegment">The EPM source path segment which points to the property we're writing. (can be null)</param> /// <param name="complexValueProjectedProperties">Set of projected properties, or null if all properties should be written.</param> /// <returns>true if anything was written, false otherwise.</returns> private bool WriteComplexValueProperty( ODataComplexValue complexValue, string propertyName, bool isTopLevel, bool isWritingCollection, Action beforeValueAction, EpmValueCache epmValueCache, IEdmTypeReference propertyTypeReference, bool isOpenPropertyType, EpmSourcePathSegment epmSourcePathSegment, ProjectedPropertiesAnnotation complexValueProjectedProperties) { // Complex properties are written recursively. DuplicatePropertyNamesChecker complexValuePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); if (isTopLevel) { // Top-level property must always write the property element Debug.Assert(complexValueProjectedProperties == null, "complexValueProjectedProperties == null"); this.WritePropertyStart(beforeValueAction, propertyName, isWritingCollection, /*isTopLevel*/ true); this.AssertRecursionDepthIsZero(); this.WriteComplexValue( complexValue, propertyTypeReference, isOpenPropertyType, isWritingCollection, null /* beforeValueAction */, null /* afterValueAction */, complexValuePropertyNamesChecker, null /* collectionValidator */, epmValueCache, epmSourcePathSegment, null /* projectedProperties */); this.AssertRecursionDepthIsZero(); this.WritePropertyEnd(); return(true); } return(this.WriteComplexValue( complexValue, propertyTypeReference, isOpenPropertyType, isWritingCollection, () => this.WritePropertyStart(beforeValueAction, propertyName, isWritingCollection, /*isTopLevel*/ false), this.WritePropertyEnd, complexValuePropertyNamesChecker, null /* collectionValidator */, epmValueCache, epmSourcePathSegment, complexValueProjectedProperties)); }
private static void CacheEpmSourcePathSegments(EpmValueCache valueCache, List <EpmSourcePathSegment> segments, IEnumerable <ODataProperty> properties) { if (properties != null) { foreach (EpmSourcePathSegment segment in segments) { ODataComplexValue value2; if ((segment.EpmInfo == null) && TryGetPropertyValue <ODataComplexValue>(properties, segment.PropertyName, out value2)) { IEnumerable <ODataProperty> enumerable = valueCache.CacheComplexValueProperties(value2); CacheEpmSourcePathSegments(valueCache, segment.SubProperties, enumerable); } } } }
/// <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="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, 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(complexValue, 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="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, 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(complexValue, writingContent)); } }
/// <summary> /// Write the given collection of properties. /// </summary> /// <param name="owningType">The <see cref="IEdmStructuredType"/> of the entry (or null if not metadata is available).</param> /// <param name="cachedProperties">Collection of cached properties for the entry.</param> /// <param name="isWritingCollection">true if we are writing a top level collection instead of an entry.</param> /// <param name="beforePropertiesAction">Action which is called before the properties are written, if there are any property.</param> /// <param name="afterPropertiesAction">Action which is called after the properties are written, if there are any property.</param> /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</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> /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param> /// <returns>true if anything was written, false otherwise.</returns> internal bool WriteProperties( IEdmStructuredType owningType, IEnumerable <ODataProperty> cachedProperties, bool isWritingCollection, Action beforePropertiesAction, Action afterPropertiesAction, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, EpmValueCache epmValueCache, EpmSourcePathSegment epmSourcePathSegment, ProjectedPropertiesAnnotation projectedProperties) { DebugUtils.CheckNoExternalCallers(); if (cachedProperties == null) { return(false); } bool propertyWritten = false; foreach (ODataProperty property in cachedProperties) { propertyWritten |= this.WriteProperty( property, owningType, /*isTopLevel*/ false, isWritingCollection, propertyWritten ? null : beforePropertiesAction, epmValueCache, epmSourcePathSegment, duplicatePropertyNamesChecker, projectedProperties); } if (afterPropertiesAction != null && propertyWritten) { afterPropertiesAction(); } return(propertyWritten); }
/// <summary> /// Reads a property value starting on a complex value. /// </summary> /// <param name="epmInfo">The EPM info which describes the mapping for which to read the property value.</param> /// <param name="complexValue">The complex value to start with.</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="complexType">The type of the complex value.</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( EntityPropertyMappingInfo epmInfo, ODataComplexValue complexValue, EpmValueCache epmValueCache, int sourceSegmentIndex, IEdmComplexTypeReference complexType) { Debug.Assert(epmInfo != null, "epmInfo != null"); Debug.Assert(epmInfo.PropertyValuePath != null, "The PropertyValuePath should have been initialized by now."); Debug.Assert(epmInfo.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(complexType != null, "complexType != null"); Debug.Assert(complexValue != null, "complexValue != null"); return(this.ReadPropertyValue( epmInfo, EpmValueCache.GetComplexValueProperties(epmValueCache, complexValue, false), sourceSegmentIndex, complexType, epmValueCache)); }
internal bool WriteComplexValue( ODataComplexValue complexValue, IEdmTypeReference metadataTypeReference, bool isOpenPropertyType, bool isWritingCollection, Action beforeValueAction, Action afterValueAction, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, EpmValueCache epmValueCache, EpmSourcePathSegment epmSourcePathSegment, ProjectedPropertiesAnnotation projectedProperties) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(complexValue != null, "complexValue != null"); string typeName = complexValue.TypeName; if (collectionValidator != null) { collectionValidator.ValidateCollectionItem(typeName, EdmTypeKind.Complex); } this.IncreaseRecursionDepth(); // resolve the type name to the type; if no type name is specified we will use the // type inferred from metadata IEdmComplexTypeReference complexTypeReference = WriterValidationUtils.ResolveTypeNameForWriting(this.Model, metadataTypeReference, ref typeName, EdmTypeKind.Complex, isOpenPropertyType).AsComplexOrNull(); // If the type is the same as the one specified by the parent collection, omit the type name, since it's not needed. if (typeName != null && collectionValidator != null) { string expectedItemTypeName = collectionValidator.ItemTypeNameFromCollection; if (string.CompareOrdinal(expectedItemTypeName, typeName) == 0) { typeName = null; } } SerializationTypeNameAnnotation serializationTypeNameAnnotation = complexValue.GetAnnotation<SerializationTypeNameAnnotation>(); if (serializationTypeNameAnnotation != null) { typeName = serializationTypeNameAnnotation.TypeName; } Action beforeValueCallbackWithTypeName = beforeValueAction; if (typeName != null) { // The beforeValueAction (if specified) will write the actual property element start. // So if we are to write the type attribute, we must postpone that after the start element was written. // And so we chain the existing action with our type attribute writing and use that // as the before action instead. if (beforeValueAction != null) { beforeValueCallbackWithTypeName = () => { beforeValueAction(); this.WritePropertyTypeAttribute(typeName); }; } else { this.WritePropertyTypeAttribute(typeName); } } // NOTE: see the comments on ODataWriterBehavior.UseV1ProviderBehavior for more information // NOTE: We have to check for ProjectedPropertiesAnnotation.Empty here to avoid filling the cache for // complex values we are writing only to ensure we don't have nested EPM-mapped null values // that will end up in the content eventually. if (this.MessageWriterSettings.WriterBehavior != null && this.MessageWriterSettings.WriterBehavior.UseV1ProviderBehavior && !object.ReferenceEquals(projectedProperties, ProjectedPropertiesAnnotation.EmptyProjectedPropertiesMarker)) { IEdmComplexType complexType = (IEdmComplexType)complexTypeReference.Definition; CachedPrimitiveKeepInContentAnnotation keepInContentCache = this.Model.EpmCachedKeepPrimitiveInContent(complexType); if (keepInContentCache == null) { // we are about to write the first value of the given type; compute the keep-in-content information for the primitive properties of this type. List<string> keepInContentPrimitiveProperties = null; // initialize the cache with all primitive properties foreach (IEdmProperty edmProperty in complexType.Properties().Where(p => p.Type.IsODataPrimitiveTypeKind())) { // figure out the keep-in-content value EntityPropertyMappingAttribute entityPropertyMapping = EpmWriterUtils.GetEntityPropertyMapping(epmSourcePathSegment, edmProperty.Name); if (entityPropertyMapping != null && entityPropertyMapping.KeepInContent) { if (keepInContentPrimitiveProperties == null) { keepInContentPrimitiveProperties = new List<string>(); } keepInContentPrimitiveProperties.Add(edmProperty.Name); } } this.Model.SetAnnotationValue<CachedPrimitiveKeepInContentAnnotation>(complexType, new CachedPrimitiveKeepInContentAnnotation(keepInContentPrimitiveProperties)); } } bool propertyWritten = this.WriteProperties( complexTypeReference == null ? null : complexTypeReference.ComplexDefinition(), EpmValueCache.GetComplexValueProperties(epmValueCache, complexValue, true), isWritingCollection, beforeValueCallbackWithTypeName, afterValueAction, duplicatePropertyNamesChecker, epmValueCache, epmSourcePathSegment, projectedProperties); this.DecreaseRecursionDepth(); return propertyWritten; }
/// <summary> /// Writes a single property in ATOM format. /// </summary> /// <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="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="beforePropertyAction">Action which is called before the property is written, if it's going to be written.</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> /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param> /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param> /// <returns>true if the property was actually written, false otherwise.</returns> private bool WriteProperty( ODataProperty property, IEdmStructuredType owningType, bool isTopLevel, bool isWritingCollection, Action beforePropertyAction, EpmValueCache epmValueCache, EpmSourcePathSegment epmParentSourcePathSegment, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, ProjectedPropertiesAnnotation projectedProperties) { DebugUtils.CheckNoExternalCallers(); WriterValidationUtils.ValidatePropertyNotNull(property); object value = property.Value; string propertyName = property.Name; EpmSourcePathSegment epmSourcePathSegment = EpmWriterUtils.GetPropertySourcePathSegment(epmParentSourcePathSegment, propertyName); //// TODO: If we implement 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. ODataComplexValue complexValue = value as ODataComplexValue; ProjectedPropertiesAnnotation complexValueProjectedProperties = null; if (!this.ShouldWritePropertyInContent(owningType, projectedProperties, propertyName, value, epmSourcePathSegment)) { // If ShouldWritePropertyInContent returns false for a comlex value we have to continue // writing the property but set the projectedProperties to an empty array. The reason for this // is that we might find EPM on a nested property that has a null value and thus must be written // in content (in which case the parent property also has to be written). // This only applies if we have EPM information for the property. if (epmSourcePathSegment != null && complexValue != null) { Debug.Assert(!projectedProperties.IsPropertyProjected(propertyName), "ShouldWritePropertyInContent must not return false for a projected complex property."); complexValueProjectedProperties = ProjectedPropertiesAnnotation.EmptyProjectedPropertiesMarker; } else { return false; } } WriterValidationUtils.ValidateProperty(property); duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property); IEdmProperty edmProperty = WriterValidationUtils.ValidatePropertyDefined(propertyName, owningType); if (value is ODataStreamReferenceValue) { throw new ODataException(o.Strings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName)); } // If the property is of Geography or Geometry type or the value is of Geography or Geometry type // make sure to check that the version is 3.0 or above. if ((edmProperty != null && edmProperty.Type.IsSpatial()) || (edmProperty == null && value is System.Spatial.ISpatial)) { ODataVersionChecker.CheckSpatialValue(this.Version); } // Null property value. if (value == null) { this.WriteNullPropertyValue(edmProperty, propertyName, isTopLevel, isWritingCollection, beforePropertyAction); return true; } bool isOpenPropertyType = owningType != null && owningType.IsOpen && edmProperty == null; if (isOpenPropertyType) { ValidationUtils.ValidateOpenPropertyValue(propertyName, value); } IEdmTypeReference propertyTypeReference = edmProperty == null ? null : edmProperty.Type; if (complexValue != null) { // Complex properties are written recursively. DuplicatePropertyNamesChecker complexValuePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); if (isTopLevel) { // Top-level property must always write the property element Debug.Assert(complexValueProjectedProperties == null, "complexValueProjectedProperties == null"); this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel); this.AssertRecursionDepthIsZero(); this.WriteComplexValue( complexValue, propertyTypeReference, isOpenPropertyType, isWritingCollection, null /* beforeValueAction */, null /* afterValueAction */, complexValuePropertyNamesChecker, null /* collectionValidator */, epmValueCache, epmSourcePathSegment, null /* projectedProperties */); this.AssertRecursionDepthIsZero(); this.WritePropertyEnd(); return true; } return this.WriteComplexValue( complexValue, propertyTypeReference, isOpenPropertyType, isWritingCollection, () => this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel), () => this.WritePropertyEnd(), complexValuePropertyNamesChecker, null /* collectionValidator */, epmValueCache, epmSourcePathSegment, complexValueProjectedProperties); } ODataCollectionValue collectionValue = value as ODataCollectionValue; if (collectionValue != null) { ODataVersionChecker.CheckCollectionValueProperties(this.Version, propertyName); this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel); this.WriteCollectionValue( collectionValue, propertyTypeReference, isOpenPropertyType, isWritingCollection); this.WritePropertyEnd(); return true; } this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel); this.WritePrimitiveValue(value, /*collectionValidator*/ null, propertyTypeReference); this.WritePropertyEnd(); return true; }
private bool WriteProperty(ODataProperty property, IEdmStructuredType owningType, bool isTopLevel, bool isWritingCollection, Action beforePropertyAction, EpmValueCache epmValueCache, EpmSourcePathSegment epmParentSourcePathSegment, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, ProjectedPropertiesAnnotation projectedProperties) { Action beforeValueAction = null; Action afterValueAction = null; WriterValidationUtils.ValidatePropertyNotNull(property); object propertyValue = property.Value; string propertyName = property.Name; EpmSourcePathSegment propertySourcePathSegment = EpmWriterUtils.GetPropertySourcePathSegment(epmParentSourcePathSegment, propertyName); ODataComplexValue complexValue = propertyValue as ODataComplexValue; ProjectedPropertiesAnnotation emptyProjectedPropertiesMarker = null; if (!this.ShouldWritePropertyInContent(owningType, projectedProperties, propertyName, propertyValue, propertySourcePathSegment)) { if ((propertySourcePathSegment == null) || (complexValue == null)) { return(false); } emptyProjectedPropertiesMarker = ProjectedPropertiesAnnotation.EmptyProjectedPropertiesMarker; } WriterValidationUtils.ValidateProperty(property); duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property); IEdmProperty edmProperty = WriterValidationUtils.ValidatePropertyDefined(propertyName, owningType); if (propertyValue is ODataStreamReferenceValue) { throw new ODataException(Microsoft.Data.OData.Strings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName)); } if (((edmProperty != null) && edmProperty.Type.IsSpatial()) || ((edmProperty == null) && (propertyValue is ISpatial))) { ODataVersionChecker.CheckSpatialValue(base.Version); } if (propertyValue == null) { this.WriteNullPropertyValue(edmProperty, propertyName, isTopLevel, isWritingCollection, beforePropertyAction); return(true); } bool isOpenPropertyType = ((owningType != null) && owningType.IsOpen) && (edmProperty == null); if (isOpenPropertyType) { ValidationUtils.ValidateOpenPropertyValue(propertyName, propertyValue); } IEdmTypeReference metadataTypeReference = (edmProperty == null) ? null : edmProperty.Type; if (complexValue != null) { DuplicatePropertyNamesChecker checker = base.CreateDuplicatePropertyNamesChecker(); if (isTopLevel) { this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel); this.WriteComplexValue(complexValue, metadataTypeReference, isOpenPropertyType, isWritingCollection, null, null, checker, null, epmValueCache, propertySourcePathSegment, null); this.WritePropertyEnd(); return(true); } if (beforeValueAction == null) { beforeValueAction = delegate { this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel); }; } if (afterValueAction == null) { afterValueAction = delegate { this.WritePropertyEnd(); }; } return(this.WriteComplexValue(complexValue, metadataTypeReference, isOpenPropertyType, isWritingCollection, beforeValueAction, afterValueAction, checker, null, epmValueCache, propertySourcePathSegment, emptyProjectedPropertiesMarker)); } ODataCollectionValue collectionValue = propertyValue as ODataCollectionValue; if (collectionValue != null) { ODataVersionChecker.CheckCollectionValueProperties(base.Version, propertyName); this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel); this.WriteCollectionValue(collectionValue, metadataTypeReference, isOpenPropertyType, isWritingCollection); this.WritePropertyEnd(); return(true); } this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel); this.WritePrimitiveValue(propertyValue, null, metadataTypeReference); this.WritePropertyEnd(); return(true); }
internal bool WriteProperties(IEdmStructuredType owningType, IEnumerable <ODataProperty> cachedProperties, bool isWritingCollection, Action beforePropertiesAction, Action afterPropertiesAction, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, EpmValueCache epmValueCache, EpmSourcePathSegment epmSourcePathSegment, ProjectedPropertiesAnnotation projectedProperties) { if (cachedProperties == null) { return(false); } bool flag = false; foreach (ODataProperty property in cachedProperties) { flag |= this.WriteProperty(property, owningType, false, isWritingCollection, flag ? null : beforePropertiesAction, epmValueCache, epmSourcePathSegment, duplicatePropertyNamesChecker, projectedProperties); } if ((afterPropertiesAction != null) && flag) { afterPropertiesAction(); } return(flag); }
internal bool WriteComplexValue(ODataComplexValue complexValue, IEdmTypeReference metadataTypeReference, bool isOpenPropertyType, bool isWritingCollection, Action beforeValueAction, Action afterValueAction, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, EpmValueCache epmValueCache, EpmSourcePathSegment epmSourcePathSegment, ProjectedPropertiesAnnotation projectedProperties) { Action action2 = null; string typeName = complexValue.TypeName; if (collectionValidator != null) { collectionValidator.ValidateCollectionItem(typeName, EdmTypeKind.Complex); } this.IncreaseRecursionDepth(); IEdmComplexTypeReference reference = WriterValidationUtils.ResolveTypeNameForWriting(base.Model, metadataTypeReference, ref typeName, EdmTypeKind.Complex, isOpenPropertyType).AsComplexOrNull(); if (((typeName != null) && (collectionValidator != null)) && (string.CompareOrdinal(collectionValidator.ItemTypeNameFromCollection, typeName) == 0)) { typeName = null; } SerializationTypeNameAnnotation annotation = complexValue.GetAnnotation <SerializationTypeNameAnnotation>(); if (annotation != null) { typeName = annotation.TypeName; } Action beforePropertiesAction = beforeValueAction; if (typeName != null) { if (beforeValueAction != null) { if (action2 == null) { action2 = delegate { beforeValueAction(); this.WritePropertyTypeAttribute(typeName); }; } beforePropertiesAction = action2; } else { this.WritePropertyTypeAttribute(typeName); } } if (((base.MessageWriterSettings.WriterBehavior != null) && base.MessageWriterSettings.WriterBehavior.UseV1ProviderBehavior) && !object.ReferenceEquals(projectedProperties, ProjectedPropertiesAnnotation.EmptyProjectedPropertiesMarker)) { IEdmComplexType definition = (IEdmComplexType)reference.Definition; if (base.Model.EpmCachedKeepPrimitiveInContent(definition) == null) { List <string> keptInContentPropertyNames = null; foreach (IEdmProperty property in from p in definition.Properties() where p.Type.IsODataPrimitiveTypeKind() select p) { EntityPropertyMappingAttribute entityPropertyMapping = EpmWriterUtils.GetEntityPropertyMapping(epmSourcePathSegment, property.Name); if ((entityPropertyMapping != null) && entityPropertyMapping.KeepInContent) { if (keptInContentPropertyNames == null) { keptInContentPropertyNames = new List <string>(); } keptInContentPropertyNames.Add(property.Name); } } base.Model.SetAnnotationValue <CachedPrimitiveKeepInContentAnnotation>(definition, new CachedPrimitiveKeepInContentAnnotation(keptInContentPropertyNames)); } } bool flag = this.WriteProperties((reference == null) ? null : reference.ComplexDefinition(), EpmValueCache.GetComplexValueProperties(epmValueCache, complexValue, true), isWritingCollection, beforePropertiesAction, afterValueAction, duplicatePropertyNamesChecker, epmValueCache, epmSourcePathSegment, projectedProperties); this.DecreaseRecursionDepth(); return(flag); }
private bool WriteProperty(ODataProperty property, IEdmStructuredType owningType, bool isTopLevel, bool isWritingCollection, Action beforePropertyAction, EpmValueCache epmValueCache, EpmSourcePathSegment epmParentSourcePathSegment, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, ProjectedPropertiesAnnotation projectedProperties) { Action beforeValueAction = null; Action afterValueAction = null; WriterValidationUtils.ValidatePropertyNotNull(property); object propertyValue = property.Value; string propertyName = property.Name; EpmSourcePathSegment propertySourcePathSegment = EpmWriterUtils.GetPropertySourcePathSegment(epmParentSourcePathSegment, propertyName); ODataComplexValue complexValue = propertyValue as ODataComplexValue; ProjectedPropertiesAnnotation emptyProjectedPropertiesMarker = null; if (!this.ShouldWritePropertyInContent(owningType, projectedProperties, propertyName, propertyValue, propertySourcePathSegment)) { if ((propertySourcePathSegment == null) || (complexValue == null)) { return false; } emptyProjectedPropertiesMarker = ProjectedPropertiesAnnotation.EmptyProjectedPropertiesMarker; } WriterValidationUtils.ValidateProperty(property); duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property); IEdmProperty edmProperty = WriterValidationUtils.ValidatePropertyDefined(propertyName, owningType); if (propertyValue is ODataStreamReferenceValue) { throw new ODataException(Microsoft.Data.OData.Strings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName)); } if (((edmProperty != null) && edmProperty.Type.IsSpatial()) || ((edmProperty == null) && (propertyValue is ISpatial))) { ODataVersionChecker.CheckSpatialValue(base.Version); } if (propertyValue == null) { this.WriteNullPropertyValue(edmProperty, propertyName, isTopLevel, isWritingCollection, beforePropertyAction); return true; } bool isOpenPropertyType = ((owningType != null) && owningType.IsOpen) && (edmProperty == null); if (isOpenPropertyType) { ValidationUtils.ValidateOpenPropertyValue(propertyName, propertyValue); } IEdmTypeReference metadataTypeReference = (edmProperty == null) ? null : edmProperty.Type; if (complexValue != null) { DuplicatePropertyNamesChecker checker = base.CreateDuplicatePropertyNamesChecker(); if (isTopLevel) { this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel); this.WriteComplexValue(complexValue, metadataTypeReference, isOpenPropertyType, isWritingCollection, null, null, checker, null, epmValueCache, propertySourcePathSegment, null); this.WritePropertyEnd(); return true; } if (beforeValueAction == null) { beforeValueAction = delegate { this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel); }; } if (afterValueAction == null) { afterValueAction = delegate { this.WritePropertyEnd(); }; } return this.WriteComplexValue(complexValue, metadataTypeReference, isOpenPropertyType, isWritingCollection, beforeValueAction, afterValueAction, checker, null, epmValueCache, propertySourcePathSegment, emptyProjectedPropertiesMarker); } ODataCollectionValue collectionValue = propertyValue as ODataCollectionValue; if (collectionValue != null) { ODataVersionChecker.CheckCollectionValueProperties(base.Version, propertyName); this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel); this.WriteCollectionValue(collectionValue, metadataTypeReference, isOpenPropertyType, isWritingCollection); this.WritePropertyEnd(); return true; } this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel); this.WritePrimitiveValue(propertyValue, null, metadataTypeReference); this.WritePropertyEnd(); return true; }
/// <summary> /// Writes a single property in ATOM format. /// </summary> /// <param name="property">The property to write out.</param> /// <param name="owningType">The owning type for the <paramref name="property"/> or null if no metadata is available.</param> /// <param name="isTopLevel">true if writing a top-level property payload; otherwise false.</param> /// <param name="isWritingCollection">true if we are writing a top-level collection instead of an entry.</param> /// <param name="beforePropertyAction">Action which is called before the property is written, if it's going to be written.</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> /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param> /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param> /// <returns>true if the property was actually written, false otherwise.</returns> private bool WriteProperty( ODataProperty property, IEdmStructuredType owningType, bool isTopLevel, bool isWritingCollection, Action beforePropertyAction, EpmValueCache epmValueCache, EpmSourcePathSegment epmParentSourcePathSegment, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, ProjectedPropertiesAnnotation projectedProperties) { DebugUtils.CheckNoExternalCallers(); WriterValidationUtils.ValidatePropertyNotNull(property); object value = property.Value; string propertyName = property.Name; EpmSourcePathSegment epmSourcePathSegment = EpmWriterUtils.GetPropertySourcePathSegment(epmParentSourcePathSegment, propertyName); //// TODO: If we implement 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. ODataComplexValue complexValue = value as ODataComplexValue; ProjectedPropertiesAnnotation complexValueProjectedProperties = null; if (!this.ShouldWritePropertyInContent(owningType, projectedProperties, propertyName, value, epmSourcePathSegment)) { // If ShouldWritePropertyInContent returns false for a comlex value we have to continue // writing the property but set the projectedProperties to an empty array. The reason for this // is that we might find EPM on a nested property that has a null value and thus must be written // in content (in which case the parent property also has to be written). // This only applies if we have EPM information for the property. if (epmSourcePathSegment != null && complexValue != null) { Debug.Assert(!projectedProperties.IsPropertyProjected(propertyName), "ShouldWritePropertyInContent must not return false for a projected complex property."); complexValueProjectedProperties = ProjectedPropertiesAnnotation.EmptyProjectedPropertiesInstance; } else { return(false); } } WriterValidationUtils.ValidatePropertyName(propertyName); duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property); IEdmProperty edmProperty = WriterValidationUtils.ValidatePropertyDefined(propertyName, owningType, this.MessageWriterSettings.UndeclaredPropertyBehaviorKinds); IEdmTypeReference propertyTypeReference = edmProperty == null ? null : edmProperty.Type; if (value is ODataStreamReferenceValue) { throw new ODataException(ODataErrorStrings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName)); } // If the property is of Geography or Geometry type or the value is of Geography or Geometry type // make sure to check that the version is 3.0 or above. if ((propertyTypeReference != null && propertyTypeReference.IsSpatial()) || (propertyTypeReference == null && value is System.Spatial.ISpatial)) { ODataVersionChecker.CheckSpatialValue(this.Version); } // Null property value. if (value == null) { this.WriteNullPropertyValue(propertyTypeReference, propertyName, isTopLevel, isWritingCollection, beforePropertyAction); return(true); } bool isOpenPropertyType = owningType != null && owningType.IsOpen && propertyTypeReference == null; if (isOpenPropertyType) { ValidationUtils.ValidateOpenPropertyValue(propertyName, value, this.MessageWriterSettings.UndeclaredPropertyBehaviorKinds); } if (complexValue != null) { return(this.WriteComplexValueProperty( complexValue, propertyName, isTopLevel, isWritingCollection, beforePropertyAction, epmValueCache, propertyTypeReference, isOpenPropertyType, epmSourcePathSegment, complexValueProjectedProperties)); } ODataCollectionValue collectionValue = value as ODataCollectionValue; if (collectionValue != null) { this.WriteCollectionValueProperty( collectionValue, propertyName, isTopLevel, isWritingCollection, beforePropertyAction, propertyTypeReference, isOpenPropertyType); return(true); } // If the value isn't one of the value types tested for already, it must be a non-null primitive. this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel); SerializationTypeNameAnnotation serializationTypeNameAnnotation = property.ODataValue.GetAnnotation <SerializationTypeNameAnnotation>(); this.WritePrimitiveValue(value, /*collectionValidator*/ null, propertyTypeReference, serializationTypeNameAnnotation); this.WritePropertyEnd(); return(true); }
private object ReadPropertyValue(EntityPropertyMappingInfo epmInfo, IEnumerable <ODataProperty> cachedProperties, int sourceSegmentIndex, IEdmStructuredTypeReference structuredTypeReference, EpmValueCache epmValueCache) { EpmSourcePathSegment segment = epmInfo.PropertyValuePath[sourceSegmentIndex]; string propertyName = segment.PropertyName; bool flag = epmInfo.PropertyValuePath.Length == (sourceSegmentIndex + 1); IEdmStructuredType owningStructuredType = structuredTypeReference.StructuredDefinition(); IEdmProperty expectedProperty = WriterValidationUtils.ValidatePropertyDefined(propertyName, owningStructuredType); if (expectedProperty != null) { if (flag) { if (!expectedProperty.Type.IsODataPrimitiveTypeKind() && !expectedProperty.Type.IsNonEntityODataCollectionTypeKind()) { throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName)); } } else if (expectedProperty.Type.TypeKind() != EdmTypeKind.Complex) { throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName)); } } ODataProperty property2 = (cachedProperties == null) ? null : cachedProperties.FirstOrDefault <ODataProperty>(p => (p.Name == propertyName)); if (property2 == null) { throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_MissingPropertyOnInstance(propertyName, structuredTypeReference.ODataFullName())); } object obj2 = property2.Value; ODataComplexValue complexValue = obj2 as ODataComplexValue; if (flag) { if (obj2 == null) { WriterValidationUtils.ValidateNullPropertyValue(expectedProperty, this.WriterBehavior, this.atomOutputContext.Model); return(obj2); } if (complexValue != null) { throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName)); } ODataCollectionValue value3 = obj2 as ODataCollectionValue; if (value3 != null) { string str = value3.TypeName; WriterValidationUtils.ResolveTypeNameForWriting(this.atomOutputContext.Model, (expectedProperty == null) ? null : expectedProperty.Type, ref str, EdmTypeKind.Collection, expectedProperty == null); return(obj2); } if (obj2 is ODataStreamReferenceValue) { throw new ODataException(Microsoft.Data.OData.Strings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName)); } if (obj2 is ISpatial) { throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_OpenPropertySpatialTypeCannotBeMapped(propertyName, epmInfo.DefiningType.FullName())); } if (expectedProperty != null) { ValidationUtils.ValidateIsExpectedPrimitiveType(obj2, expectedProperty.Type); } return(obj2); } if (complexValue == null) { if (obj2 != null) { throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName)); } return(null); } string typeName = complexValue.TypeName; IEdmComplexTypeReference complexType = WriterValidationUtils.ResolveTypeNameForWriting(this.atomOutputContext.Model, (expectedProperty == null) ? null : expectedProperty.Type, ref typeName, EdmTypeKind.Complex, expectedProperty == null).AsComplexOrNull(); return(this.ReadComplexPropertyValue(epmInfo, complexValue, epmValueCache, sourceSegmentIndex + 1, complexType)); }
private object ReadComplexPropertyValue(EntityPropertyMappingInfo epmInfo, ODataComplexValue complexValue, EpmValueCache epmValueCache, int sourceSegmentIndex, IEdmComplexTypeReference complexType) { return this.ReadPropertyValue(epmInfo, EpmValueCache.GetComplexValueProperties(epmValueCache, complexValue, false), sourceSegmentIndex, complexType, epmValueCache); }
/// <summary> /// Reads a property value starting on a complex value. /// </summary> /// <param name="epmInfo">The EPM info which describes the mapping for which to read the property value.</param> /// <param name="complexValue">The complex value to start with.</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="complexType">The type of the complex value.</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( EntityPropertyMappingInfo epmInfo, ODataComplexValue complexValue, EpmValueCache epmValueCache, int sourceSegmentIndex, IEdmComplexTypeReference complexType) { Debug.Assert(epmInfo != null, "epmInfo != null"); Debug.Assert(epmInfo.PropertyValuePath != null, "The PropertyValuePath should have been initialized by now."); Debug.Assert(epmInfo.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(complexType != null, "complexType != null"); Debug.Assert(complexValue != null, "complexValue != null"); return this.ReadPropertyValue( epmInfo, EpmValueCache.GetComplexValueProperties(epmValueCache, complexValue, false), sourceSegmentIndex, complexType, epmValueCache); }
/// <summary> /// Reads a property value starting with the specified index to the property value path. /// </summary> /// <param name="epmInfo">The EPM info which describes the mapping for which to read the property value.</param> /// <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="structuredTypeReference">The type of the entry or complex value the <paramref name="cachedProperties"/> enumeration belongs to.</param> /// <param name="epmValueCache">The EPM value cache to use.</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( EntityPropertyMappingInfo epmInfo, IEnumerable<ODataProperty> cachedProperties, int sourceSegmentIndex, IEdmStructuredTypeReference structuredTypeReference, EpmValueCache epmValueCache) { Debug.Assert(epmInfo != null, "epmInfo != null"); Debug.Assert(epmInfo.PropertyValuePath != null, "The PropertyValuePath should have been initialized by now."); Debug.Assert(epmInfo.PropertyValuePath.Length > sourceSegmentIndex, "The PropertyValuePath must be at least as long as the source segment index."); Debug.Assert(structuredTypeReference != null, "structuredTypeReference != null"); Debug.Assert(epmValueCache != null, "epmValueCache != null"); EpmSourcePathSegment sourceSegment = epmInfo.PropertyValuePath[sourceSegmentIndex]; string propertyName = sourceSegment.PropertyName; bool lastSegment = epmInfo.PropertyValuePath.Length == sourceSegmentIndex + 1; IEdmStructuredType structuredType = structuredTypeReference.StructuredDefinition(); IEdmProperty edmProperty = WriterValidationUtils.ValidatePropertyDefined(propertyName, structuredType); if (edmProperty != null) { // If this is the last part of the path, then it has to be a primitive or atomic collection type otherwise should be a complex type if (lastSegment) { if (!edmProperty.Type.IsODataPrimitiveTypeKind() && !edmProperty.Type.IsNonEntityODataCollectionTypeKind()) { throw new ODataException(o.Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName)); } } else { if (edmProperty.Type.TypeKind() != EdmTypeKind.Complex) { throw new ODataException(o.Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName)); } } } else { Debug.Assert( structuredType.IsOpen, "Only open types can have undeclared properties, otherwise we should have failed in the ValidatePropertyDefined method."); } ODataProperty property = cachedProperties == null ? null : cachedProperties.FirstOrDefault(p => p.Name == propertyName); if (property == null) { throw new ODataException(o.Strings.EpmSourceTree_MissingPropertyOnInstance(propertyName, structuredTypeReference.ODataFullName())); } object propertyValue = property.Value; ODataComplexValue propertyComplexValue = propertyValue as ODataComplexValue; if (lastSegment) { if (propertyValue == null) { WriterValidationUtils.ValidateNullPropertyValue(edmProperty, this.WriterBehavior, this.atomOutputContext.Model); } else { // If this property is the last one it has to be either a primitive or collection if (propertyComplexValue != null) { throw new ODataException(o.Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName)); } else { ODataCollectionValue propertyCollectionValue = propertyValue as ODataCollectionValue; if (propertyCollectionValue != null) { // Validate the type name for the collection string typeName = propertyCollectionValue.TypeName; WriterValidationUtils.ResolveTypeNameForWriting( this.atomOutputContext.Model, edmProperty == null ? null : edmProperty.Type, ref typeName, EdmTypeKind.Collection, edmProperty == null); } else { if (propertyValue is ODataStreamReferenceValue) { // Stream properties should not come here, if it were an ODataEntry property it would have been // filtered in ReadEntryPropertyValue() by "epmValueCache.EntryProperties" call. throw new ODataException(o.Strings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName)); } else if (propertyValue is ISpatial) { throw new ODataException(o.Strings.EpmSourceTree_OpenPropertySpatialTypeCannotBeMapped(propertyName, epmInfo.DefiningType.FullName())); } else if (edmProperty != null) { ValidationUtils.ValidateIsExpectedPrimitiveType(propertyValue, edmProperty.Type); } } } } return propertyValue; } // Otherwise it's in the middle and thus it must be a complex value if (propertyComplexValue == null) { if (propertyValue != null) { // It's not a complex value - fail. throw new ODataException(o.Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName)); } else { // The value of the property is null, which can be a null complex value // Note that we must not attempt to resolve the type as if the type name was null here, because // 1) We don't need the type for anything anyway (the value is null, this is the end) // 2) If the property is open, trying to resolve a null type name would throw // but we don't have a null type name, we have a null entire value. return null; } } string localTypeName = propertyComplexValue.TypeName; IEdmComplexTypeReference complexValueType = WriterValidationUtils.ResolveTypeNameForWriting( this.atomOutputContext.Model, edmProperty == null ? null : edmProperty.Type, ref localTypeName, EdmTypeKind.Complex, edmProperty == null).AsComplexOrNull(); return this.ReadComplexPropertyValue( epmInfo, propertyComplexValue, epmValueCache, sourceSegmentIndex + 1, complexValueType); }
internal bool WriteComplexValue(ODataComplexValue complexValue, IEdmTypeReference metadataTypeReference, bool isOpenPropertyType, bool isWritingCollection, Action beforeValueAction, Action afterValueAction, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, EpmValueCache epmValueCache, EpmSourcePathSegment epmSourcePathSegment, ProjectedPropertiesAnnotation projectedProperties) { Action action2 = null; string typeName = complexValue.TypeName; if (collectionValidator != null) { collectionValidator.ValidateCollectionItem(typeName, EdmTypeKind.Complex); } this.IncreaseRecursionDepth(); IEdmComplexTypeReference reference = WriterValidationUtils.ResolveTypeNameForWriting(base.Model, metadataTypeReference, ref typeName, EdmTypeKind.Complex, isOpenPropertyType).AsComplexOrNull(); if (((typeName != null) && (collectionValidator != null)) && (string.CompareOrdinal(collectionValidator.ItemTypeNameFromCollection, typeName) == 0)) { typeName = null; } SerializationTypeNameAnnotation annotation = complexValue.GetAnnotation<SerializationTypeNameAnnotation>(); if (annotation != null) { typeName = annotation.TypeName; } Action beforePropertiesAction = beforeValueAction; if (typeName != null) { if (beforeValueAction != null) { if (action2 == null) { action2 = delegate { beforeValueAction(); this.WritePropertyTypeAttribute(typeName); }; } beforePropertiesAction = action2; } else { this.WritePropertyTypeAttribute(typeName); } } if (((base.MessageWriterSettings.WriterBehavior != null) && base.MessageWriterSettings.WriterBehavior.UseV1ProviderBehavior) && !object.ReferenceEquals(projectedProperties, ProjectedPropertiesAnnotation.EmptyProjectedPropertiesMarker)) { IEdmComplexType definition = (IEdmComplexType) reference.Definition; if (base.Model.EpmCachedKeepPrimitiveInContent(definition) == null) { List<string> keptInContentPropertyNames = null; foreach (IEdmProperty property in from p in definition.Properties() where p.Type.IsODataPrimitiveTypeKind() select p) { EntityPropertyMappingAttribute entityPropertyMapping = EpmWriterUtils.GetEntityPropertyMapping(epmSourcePathSegment, property.Name); if ((entityPropertyMapping != null) && entityPropertyMapping.KeepInContent) { if (keptInContentPropertyNames == null) { keptInContentPropertyNames = new List<string>(); } keptInContentPropertyNames.Add(property.Name); } } base.Model.SetAnnotationValue<CachedPrimitiveKeepInContentAnnotation>(definition, new CachedPrimitiveKeepInContentAnnotation(keptInContentPropertyNames)); } } bool flag = this.WriteProperties((reference == null) ? null : reference.ComplexDefinition(), EpmValueCache.GetComplexValueProperties(epmValueCache, complexValue, true), isWritingCollection, beforePropertiesAction, afterValueAction, duplicatePropertyNamesChecker, epmValueCache, epmSourcePathSegment, projectedProperties); this.DecreaseRecursionDepth(); return flag; }
/// <summary> /// Reads a property value starting with the specified index to the property value path. /// </summary> /// <param name="epmInfo">The EPM info which describes the mapping for which to read the property value.</param> /// <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="structuredTypeReference">The type of the entry or complex value the <paramref name="cachedProperties"/> enumeration belongs to.</param> /// <param name="epmValueCache">The EPM value cache to use.</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( EntityPropertyMappingInfo epmInfo, IEnumerable <ODataProperty> cachedProperties, int sourceSegmentIndex, IEdmStructuredTypeReference structuredTypeReference, EpmValueCache epmValueCache) { Debug.Assert(epmInfo != null, "epmInfo != null"); Debug.Assert(epmInfo.PropertyValuePath != null, "The PropertyValuePath should have been initialized by now."); Debug.Assert(epmInfo.PropertyValuePath.Length > sourceSegmentIndex, "The PropertyValuePath must be at least as long as the source segment index."); Debug.Assert(structuredTypeReference != null, "structuredTypeReference != null"); Debug.Assert(epmValueCache != null, "epmValueCache != null"); EpmSourcePathSegment sourceSegment = epmInfo.PropertyValuePath[sourceSegmentIndex]; string propertyName = sourceSegment.PropertyName; bool lastSegment = epmInfo.PropertyValuePath.Length == sourceSegmentIndex + 1; IEdmStructuredType structuredType = structuredTypeReference.StructuredDefinition(); IEdmProperty edmProperty = WriterValidationUtils.ValidatePropertyDefined(propertyName, structuredType); if (edmProperty != null) { // If this is the last part of the path, then it has to be a primitive or atomic collection type otherwise should be a complex type if (lastSegment) { if (!edmProperty.Type.IsODataPrimitiveTypeKind() && !edmProperty.Type.IsNonEntityODataCollectionTypeKind()) { throw new ODataException(o.Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName)); } } else { if (edmProperty.Type.TypeKind() != EdmTypeKind.Complex) { throw new ODataException(o.Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName)); } } } else { Debug.Assert( structuredType.IsOpen, "Only open types can have undeclared properties, otherwise we should have failed in the ValidatePropertyDefined method."); } ODataProperty property = cachedProperties == null ? null : cachedProperties.FirstOrDefault(p => p.Name == propertyName); if (property == null) { throw new ODataException(o.Strings.EpmSourceTree_MissingPropertyOnInstance(propertyName, structuredTypeReference.ODataFullName())); } object propertyValue = property.Value; ODataComplexValue propertyComplexValue = propertyValue as ODataComplexValue; if (lastSegment) { if (propertyValue == null) { WriterValidationUtils.ValidateNullPropertyValue(edmProperty, this.WriterBehavior, this.atomOutputContext.Model); } else { // If this property is the last one it has to be either a primitive or collection if (propertyComplexValue != null) { throw new ODataException(o.Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName)); } else { ODataCollectionValue propertyCollectionValue = propertyValue as ODataCollectionValue; if (propertyCollectionValue != null) { // Validate the type name for the collection string typeName = propertyCollectionValue.TypeName; WriterValidationUtils.ResolveTypeNameForWriting( this.atomOutputContext.Model, edmProperty == null ? null : edmProperty.Type, ref typeName, EdmTypeKind.Collection, edmProperty == null); } else { if (propertyValue is ODataStreamReferenceValue) { // Stream properties should not come here, if it were an ODataEntry property it would have been // filtered in ReadEntryPropertyValue() by "epmValueCache.EntryProperties" call. throw new ODataException(o.Strings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName)); } else if (propertyValue is ISpatial) { throw new ODataException(o.Strings.EpmSourceTree_OpenPropertySpatialTypeCannotBeMapped(propertyName, epmInfo.DefiningType.FullName())); } else if (edmProperty != null) { ValidationUtils.ValidateIsExpectedPrimitiveType(propertyValue, edmProperty.Type); } } } } return(propertyValue); } // Otherwise it's in the middle and thus it must be a complex value if (propertyComplexValue == null) { if (propertyValue != null) { // It's not a complex value - fail. throw new ODataException(o.Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName)); } else { // The value of the property is null, which can be a null complex value // Note that we must not attempt to resolve the type as if the type name was null here, because // 1) We don't need the type for anything anyway (the value is null, this is the end) // 2) If the property is open, trying to resolve a null type name would throw // but we don't have a null type name, we have a null entire value. return(null); } } string localTypeName = propertyComplexValue.TypeName; IEdmComplexTypeReference complexValueType = WriterValidationUtils.ResolveTypeNameForWriting( this.atomOutputContext.Model, edmProperty == null ? null : edmProperty.Type, ref localTypeName, EdmTypeKind.Complex, edmProperty == null).AsComplexOrNull(); return(this.ReadComplexPropertyValue( epmInfo, propertyComplexValue, epmValueCache, sourceSegmentIndex + 1, complexValueType)); }
internal bool WriteComplexValue( ODataComplexValue complexValue, IEdmTypeReference metadataTypeReference, bool isOpenPropertyType, bool isWritingCollection, Action beforeValueAction, Action afterValueAction, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, EpmValueCache epmValueCache, EpmSourcePathSegment epmSourcePathSegment, ProjectedPropertiesAnnotation projectedProperties) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(complexValue != null, "complexValue != null"); string typeName = complexValue.TypeName; if (collectionValidator != null) { collectionValidator.ValidateCollectionItem(typeName, EdmTypeKind.Complex); } this.IncreaseRecursionDepth(); // resolve the type name to the type; if no type name is specified we will use the // type inferred from metadata IEdmComplexTypeReference complexTypeReference = TypeNameOracle.ResolveAndValidateTypeNameForValue(this.Model, metadataTypeReference, complexValue, isOpenPropertyType).AsComplexOrNull(); string collectionItemTypeName; typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(complexValue, complexTypeReference, complexValue.GetAnnotation <SerializationTypeNameAnnotation>(), collectionValidator, out collectionItemTypeName); Debug.Assert(collectionItemTypeName == null, "collectionItemTypeName == null"); Action beforeValueCallbackWithTypeName = beforeValueAction; if (typeName != null) { // The beforeValueAction (if specified) will write the actual property element start. // So if we are to write the type attribute, we must postpone that after the start element was written. // And so we chain the existing action with our type attribute writing and use that // as the before action instead. if (beforeValueAction != null) { beforeValueCallbackWithTypeName = () => { beforeValueAction(); this.WritePropertyTypeAttribute(typeName); }; } else { this.WritePropertyTypeAttribute(typeName); } } // NOTE: see the comments on ODataWriterBehavior.UseV1ProviderBehavior for more information // NOTE: We have to check for ProjectedPropertiesAnnotation.Empty here to avoid filling the cache for // complex values we are writing only to ensure we don't have nested EPM-mapped null values // that will end up in the content eventually. if (this.MessageWriterSettings.WriterBehavior != null && this.MessageWriterSettings.WriterBehavior.UseV1ProviderBehavior && !object.ReferenceEquals(projectedProperties, ProjectedPropertiesAnnotation.EmptyProjectedPropertiesInstance)) { IEdmComplexType complexType = (IEdmComplexType)complexTypeReference.Definition; CachedPrimitiveKeepInContentAnnotation keepInContentCache = this.Model.EpmCachedKeepPrimitiveInContent(complexType); if (keepInContentCache == null) { // we are about to write the first value of the given type; compute the keep-in-content information for the primitive properties of this type. List <string> keepInContentPrimitiveProperties = null; // initialize the cache with all primitive properties foreach (IEdmProperty edmProperty in complexType.Properties().Where(p => p.Type.IsODataPrimitiveTypeKind())) { // figure out the keep-in-content value EntityPropertyMappingAttribute entityPropertyMapping = EpmWriterUtils.GetEntityPropertyMapping(epmSourcePathSegment, edmProperty.Name); if (entityPropertyMapping != null && entityPropertyMapping.KeepInContent) { if (keepInContentPrimitiveProperties == null) { keepInContentPrimitiveProperties = new List <string>(); } keepInContentPrimitiveProperties.Add(edmProperty.Name); } } this.Model.SetAnnotationValue <CachedPrimitiveKeepInContentAnnotation>(complexType, new CachedPrimitiveKeepInContentAnnotation(keepInContentPrimitiveProperties)); } } bool propertyWritten = this.WriteProperties( complexTypeReference == null ? null : complexTypeReference.ComplexDefinition(), EpmValueCache.GetComplexValueProperties(epmValueCache, complexValue, true), isWritingCollection, beforeValueCallbackWithTypeName, afterValueAction, duplicatePropertyNamesChecker, epmValueCache, epmSourcePathSegment, projectedProperties); this.DecreaseRecursionDepth(); return(propertyWritten); }
/// <summary> /// Write the given collection of properties. /// </summary> /// <param name="owningType">The <see cref="IEdmStructuredType"/> of the entry (or null if not metadata is available).</param> /// <param name="cachedProperties">Collection of cached properties for the entry.</param> /// <param name="isWritingCollection">true if we are writing a collection instead of an entry.</param> /// <param name="beforePropertiesAction">Action which is called before the properties are written, if there are any property.</param> /// <param name="afterPropertiesAction">Action which is called after the properties are written, if there are any property.</param> /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</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> /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param> /// <returns>true if anything was written, false otherwise.</returns> internal bool WriteProperties( IEdmStructuredType owningType, IEnumerable<ODataProperty> cachedProperties, bool isWritingCollection, Action beforePropertiesAction, Action afterPropertiesAction, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, EpmValueCache epmValueCache, EpmSourcePathSegment epmSourcePathSegment, ProjectedPropertiesAnnotation projectedProperties) { DebugUtils.CheckNoExternalCallers(); if (cachedProperties == null) { return false; } bool propertyWritten = false; foreach (ODataProperty property in cachedProperties) { propertyWritten |= this.WriteProperty( property, owningType, false, isWritingCollection, propertyWritten ? null : beforePropertiesAction, epmValueCache, epmSourcePathSegment, duplicatePropertyNamesChecker, projectedProperties); } if (afterPropertiesAction != null && propertyWritten) { afterPropertiesAction(); } return propertyWritten; }
/// <summary> /// Writes a single property in ATOM format. /// </summary> /// <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="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="beforePropertyAction">Action which is called before the property is written, if it's going to be written.</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> /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param> /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param> /// <returns>true if the property was actually written, false otherwise.</returns> private bool WriteProperty( ODataProperty property, IEdmStructuredType owningType, bool isTopLevel, bool isWritingCollection, Action beforePropertyAction, EpmValueCache epmValueCache, EpmSourcePathSegment epmParentSourcePathSegment, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, ProjectedPropertiesAnnotation projectedProperties) { DebugUtils.CheckNoExternalCallers(); WriterValidationUtils.ValidatePropertyNotNull(property); object value = property.Value; string propertyName = property.Name; EpmSourcePathSegment epmSourcePathSegment = EpmWriterUtils.GetPropertySourcePathSegment(epmParentSourcePathSegment, propertyName); //// TODO: If we implement 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. ODataComplexValue complexValue = value as ODataComplexValue; ProjectedPropertiesAnnotation complexValueProjectedProperties = null; if (!this.ShouldWritePropertyInContent(owningType, projectedProperties, propertyName, value, epmSourcePathSegment)) { // If ShouldWritePropertyInContent returns false for a comlex value we have to continue // writing the property but set the projectedProperties to an empty array. The reason for this // is that we might find EPM on a nested property that has a null value and thus must be written // in content (in which case the parent property also has to be written). // This only applies if we have EPM information for the property. if (epmSourcePathSegment != null && complexValue != null) { Debug.Assert(!projectedProperties.IsPropertyProjected(propertyName), "ShouldWritePropertyInContent must not return false for a projected complex property."); complexValueProjectedProperties = ProjectedPropertiesAnnotation.EmptyProjectedPropertiesMarker; } else { return(false); } } WriterValidationUtils.ValidateProperty(property); duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property); IEdmProperty edmProperty = WriterValidationUtils.ValidatePropertyDefined(propertyName, owningType); if (value is ODataStreamReferenceValue) { throw new ODataException(o.Strings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName)); } // If the property is of Geography or Geometry type or the value is of Geography or Geometry type // make sure to check that the version is 3.0 or above. if ((edmProperty != null && edmProperty.Type.IsSpatial()) || (edmProperty == null && value is System.Spatial.ISpatial)) { ODataVersionChecker.CheckSpatialValue(this.Version); } // Null property value. if (value == null) { this.WriteNullPropertyValue(edmProperty, propertyName, isTopLevel, isWritingCollection, beforePropertyAction); return(true); } bool isOpenPropertyType = owningType != null && owningType.IsOpen && edmProperty == null; if (isOpenPropertyType) { ValidationUtils.ValidateOpenPropertyValue(propertyName, value); } IEdmTypeReference propertyTypeReference = edmProperty == null ? null : edmProperty.Type; if (complexValue != null) { // Complex properties are written recursively. DuplicatePropertyNamesChecker complexValuePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); if (isTopLevel) { // Top-level property must always write the property element Debug.Assert(complexValueProjectedProperties == null, "complexValueProjectedProperties == null"); this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel); this.AssertRecursionDepthIsZero(); this.WriteComplexValue( complexValue, propertyTypeReference, isOpenPropertyType, isWritingCollection, null /* beforeValueAction */, null /* afterValueAction */, complexValuePropertyNamesChecker, null /* collectionValidator */, epmValueCache, epmSourcePathSegment, null /* projectedProperties */); this.AssertRecursionDepthIsZero(); this.WritePropertyEnd(); return(true); } return(this.WriteComplexValue( complexValue, propertyTypeReference, isOpenPropertyType, isWritingCollection, () => this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel), () => this.WritePropertyEnd(), complexValuePropertyNamesChecker, null /* collectionValidator */, epmValueCache, epmSourcePathSegment, complexValueProjectedProperties)); } ODataCollectionValue collectionValue = value as ODataCollectionValue; if (collectionValue != null) { ODataVersionChecker.CheckCollectionValueProperties(this.Version, propertyName); this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel); this.WriteCollectionValue( collectionValue, propertyTypeReference, isOpenPropertyType, isWritingCollection); this.WritePropertyEnd(); return(true); } this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel); this.WritePrimitiveValue(value, /*collectionValidator*/ null, propertyTypeReference); this.WritePropertyEnd(); return(true); }
internal bool WriteProperties(IEdmStructuredType owningType, IEnumerable<ODataProperty> cachedProperties, bool isWritingCollection, Action beforePropertiesAction, Action afterPropertiesAction, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, EpmValueCache epmValueCache, EpmSourcePathSegment epmSourcePathSegment, ProjectedPropertiesAnnotation projectedProperties) { if (cachedProperties == null) { return false; } bool flag = false; foreach (ODataProperty property in cachedProperties) { flag |= this.WriteProperty(property, owningType, false, isWritingCollection, flag ? null : beforePropertiesAction, epmValueCache, epmSourcePathSegment, duplicatePropertyNamesChecker, projectedProperties); } if ((afterPropertiesAction != null) && flag) { afterPropertiesAction(); } return flag; }
private object ReadPropertyValue(EntityPropertyMappingInfo epmInfo, IEnumerable<ODataProperty> cachedProperties, int sourceSegmentIndex, IEdmStructuredTypeReference structuredTypeReference, EpmValueCache epmValueCache) { EpmSourcePathSegment segment = epmInfo.PropertyValuePath[sourceSegmentIndex]; string propertyName = segment.PropertyName; bool flag = epmInfo.PropertyValuePath.Length == (sourceSegmentIndex + 1); IEdmStructuredType owningStructuredType = structuredTypeReference.StructuredDefinition(); IEdmProperty expectedProperty = WriterValidationUtils.ValidatePropertyDefined(propertyName, owningStructuredType); if (expectedProperty != null) { if (flag) { if (!expectedProperty.Type.IsODataPrimitiveTypeKind() && !expectedProperty.Type.IsNonEntityODataCollectionTypeKind()) { throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName)); } } else if (expectedProperty.Type.TypeKind() != EdmTypeKind.Complex) { throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName)); } } ODataProperty property2 = (cachedProperties == null) ? null : cachedProperties.FirstOrDefault<ODataProperty>(p => (p.Name == propertyName)); if (property2 == null) { throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_MissingPropertyOnInstance(propertyName, structuredTypeReference.ODataFullName())); } object obj2 = property2.Value; ODataComplexValue complexValue = obj2 as ODataComplexValue; if (flag) { if (obj2 == null) { WriterValidationUtils.ValidateNullPropertyValue(expectedProperty, this.WriterBehavior, this.atomOutputContext.Model); return obj2; } if (complexValue != null) { throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName)); } ODataCollectionValue value3 = obj2 as ODataCollectionValue; if (value3 != null) { string str = value3.TypeName; WriterValidationUtils.ResolveTypeNameForWriting(this.atomOutputContext.Model, (expectedProperty == null) ? null : expectedProperty.Type, ref str, EdmTypeKind.Collection, expectedProperty == null); return obj2; } if (obj2 is ODataStreamReferenceValue) { throw new ODataException(Microsoft.Data.OData.Strings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName)); } if (obj2 is ISpatial) { throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_OpenPropertySpatialTypeCannotBeMapped(propertyName, epmInfo.DefiningType.FullName())); } if (expectedProperty != null) { ValidationUtils.ValidateIsExpectedPrimitiveType(obj2, expectedProperty.Type); } return obj2; } if (complexValue == null) { if (obj2 != null) { throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName)); } return null; } string typeName = complexValue.TypeName; IEdmComplexTypeReference complexType = WriterValidationUtils.ResolveTypeNameForWriting(this.atomOutputContext.Model, (expectedProperty == null) ? null : expectedProperty.Type, ref typeName, EdmTypeKind.Complex, expectedProperty == null).AsComplexOrNull(); return this.ReadComplexPropertyValue(epmInfo, complexValue, epmValueCache, sourceSegmentIndex + 1, complexType); }
private object ReadComplexPropertyValue(EntityPropertyMappingInfo epmInfo, ODataComplexValue complexValue, EpmValueCache epmValueCache, int sourceSegmentIndex, IEdmComplexTypeReference complexType) { return(this.ReadPropertyValue(epmInfo, EpmValueCache.GetComplexValueProperties(epmValueCache, complexValue, false), sourceSegmentIndex, complexType, epmValueCache)); }