private void ReadParentSegment(EpmTargetPathSegment targetSegment, AtomEntryMetadata entryMetadata)
        {
            switch (targetSegment.SegmentName)
            {
            case "author":
            {
                AtomPersonMetadata personMetadata = entryMetadata.Authors.FirstOrDefault <AtomPersonMetadata>();
                if (personMetadata != null)
                {
                    this.ReadPersonEpm(ReaderUtils.GetPropertiesList(base.EntryState.Entry.Properties), base.EntryState.EntityType.ToTypeReference(), targetSegment, personMetadata);
                }
                return;
            }

            case "contributor":
            {
                AtomPersonMetadata metadata2 = entryMetadata.Contributors.FirstOrDefault <AtomPersonMetadata>();
                if (metadata2 != null)
                {
                    this.ReadPersonEpm(ReaderUtils.GetPropertiesList(base.EntryState.Entry.Properties), base.EntryState.EntityType.ToTypeReference(), targetSegment, metadata2);
                }
                return;
            }
            }
            throw new ODataException(Microsoft.Data.OData.Strings.General_InternalError(InternalErrorCodes.EpmSyndicationReader_ReadParentSegment_TargetSegmentName));
        }
Beispiel #2
0
 /// <summary>
 /// Sets the value read from EPM to a property on an entry.
 /// </summary>
 /// <param name="epmInfo">The EPM info for the mapping for which the value was read.</param>
 /// <param name="propertyValue">The property value read, if the value was specified as null then this should be null,
 /// if the value was missing the method should not be called at all.
 /// For primitive properties this should be the string value, for all other properties this should be the exact value type.</param>
 protected void SetEntryEpmValue(EntityPropertyMappingInfo epmInfo, object propertyValue)
 {
     this.SetEpmValue(
         ReaderUtils.GetPropertiesList(this.entryState.Entry.Properties),
         this.entryState.EntityType.ToTypeReference(),
         epmInfo,
         propertyValue);
 }
        /// <summary>
        /// Reads a non-leaf segment which has sub segments.
        /// </summary>
        /// <param name="targetSegment">The segment being read.</param>
        /// <param name="entryMetadata">The ATOM entry metadata to read from.</param>
        private void ReadParentSegment(EpmTargetPathSegment targetSegment, AtomEntryMetadata entryMetadata)
        {
            Debug.Assert(targetSegment != null, "targetSegment != null");
            Debug.Assert(entryMetadata != null, "entryMetadata != null");
            switch (targetSegment.SegmentName)
            {
            case AtomConstants.AtomAuthorElementName:
                // If a singleton property (non-collection) is mapped to author and there are multiple authors
                // EPM uses the first author.
                AtomPersonMetadata authorMetadata = entryMetadata.Authors.FirstOrDefault();
                if (authorMetadata != null)
                {
                    this.ReadPersonEpm(
                        ReaderUtils.GetPropertiesList(this.EntryState.Entry.Properties),
                        this.EntryState.EntityType.ToTypeReference(),
                        targetSegment,
                        authorMetadata);
                }

                break;

            case AtomConstants.AtomContributorElementName:
                // If a singleton property (non-collection) is mapped to contributor and there are multiple contributors
                // EPM uses the first contributor.
                AtomPersonMetadata contributorMetadata = entryMetadata.Contributors.FirstOrDefault();
                if (contributorMetadata != null)
                {
                    this.ReadPersonEpm(
                        ReaderUtils.GetPropertiesList(this.EntryState.Entry.Properties),
                        this.EntryState.EntityType.ToTypeReference(),
                        targetSegment,
                        contributorMetadata);
                }

                break;

            default:
                throw new ODataException(o.Strings.General_InternalError(InternalErrorCodes.EpmSyndicationReader_ReadParentSegment_TargetSegmentName));
            }
        }
Beispiel #4
0
        private void SetEpmValueForSegment(EntityPropertyMappingInfo epmInfo, int propertyValuePathIndex, IEdmStructuredTypeReference segmentStructuralTypeReference, List <ODataProperty> existingProperties, object propertyValue)
        {
            string propertyName = epmInfo.PropertyValuePath[propertyValuePathIndex].PropertyName;

            if (!epmInfo.Attribute.KeepInContent)
            {
                IEdmTypeReference type;
                ODataProperty     property = existingProperties.FirstOrDefault <ODataProperty>(p => string.CompareOrdinal(p.Name, propertyName) == 0);
                ODataComplexValue value2   = null;
                if (property != null)
                {
                    value2 = property.Value as ODataComplexValue;
                    if (value2 == null)
                    {
                        return;
                    }
                }
                IEdmProperty property2 = segmentStructuralTypeReference.FindProperty(propertyName);
                if ((property2 == null) && (propertyValuePathIndex != (epmInfo.PropertyValuePath.Length - 1)))
                {
                    throw new ODataException(Microsoft.Data.OData.Strings.EpmReader_OpenComplexOrCollectionEpmProperty(epmInfo.Attribute.SourcePath));
                }
                if ((property2 == null) || (this.MessageReaderSettings.DisablePrimitiveTypeConversion && property2.Type.IsODataPrimitiveTypeKind()))
                {
                    type = EdmCoreModel.Instance.GetString(true);
                }
                else
                {
                    type = property2.Type;
                }
                switch (type.TypeKind())
                {
                case EdmTypeKind.Primitive:
                    object obj2;
                    if (type.IsStream())
                    {
                        throw new ODataException(Microsoft.Data.OData.Strings.General_InternalError(InternalErrorCodes.EpmReader_SetEpmValueForSegment_StreamProperty));
                    }
                    if (propertyValue == null)
                    {
                        ReaderValidationUtils.ValidateNullValue(this.atomInputContext.Model, type, this.atomInputContext.MessageReaderSettings, true, this.atomInputContext.Version);
                        obj2 = null;
                    }
                    else
                    {
                        obj2 = AtomValueUtils.ConvertStringToPrimitive((string)propertyValue, type.AsPrimitive());
                    }
                    this.AddEpmPropertyValue(existingProperties, propertyName, obj2, segmentStructuralTypeReference.IsODataEntityTypeKind());
                    return;

                case EdmTypeKind.Complex:
                {
                    if (value2 == null)
                    {
                        value2 = new ODataComplexValue {
                            TypeName   = type.ODataFullName(),
                            Properties = new ReadOnlyEnumerable <ODataProperty>()
                        };
                        this.AddEpmPropertyValue(existingProperties, propertyName, value2, segmentStructuralTypeReference.IsODataEntityTypeKind());
                    }
                    IEdmComplexTypeReference reference2 = type.AsComplex();
                    this.SetEpmValueForSegment(epmInfo, propertyValuePathIndex + 1, reference2, ReaderUtils.GetPropertiesList(value2.Properties), propertyValue);
                    return;
                }

                case EdmTypeKind.Collection:
                {
                    ODataCollectionValue value4 = new ODataCollectionValue {
                        TypeName = type.ODataFullName(),
                        Items    = new ReadOnlyEnumerable((List <object>)propertyValue)
                    };
                    this.AddEpmPropertyValue(existingProperties, propertyName, value4, segmentStructuralTypeReference.IsODataEntityTypeKind());
                    return;
                }
                }
                throw new ODataException(Microsoft.Data.OData.Strings.General_InternalError(InternalErrorCodes.EpmReader_SetEpmValueForSegment_TypeKind));
            }
        }
Beispiel #5
0
        private void SetEpmValueForSegment(
            EntityPropertyMappingInfo epmInfo,
            int propertyValuePathIndex,
            IEdmStructuredTypeReference segmentStructuralTypeReference,
            List <ODataProperty> existingProperties,
            object propertyValue)
        {
            Debug.Assert(epmInfo != null, "epmInfo != null");
            Debug.Assert(propertyValuePathIndex < epmInfo.PropertyValuePath.Length, "The propertyValuePathIndex is out of bounds.");
            Debug.Assert(existingProperties != null, "existingProperties != null");

            string propertyName = epmInfo.PropertyValuePath[propertyValuePathIndex].PropertyName;

            // Do not set out-of-content values if the EPM is defined as KeepInContent=true.
            if (epmInfo.Attribute.KeepInContent)
            {
                return;
            }

            // Try to find the property in the existing properties
            // If the property value is atomic from point of view of EPM (non-streaming collection or primitive) then if it already exists
            // it must have been in-content, and thus we leave it as is (note that two EPMs can't map to the same property, we verify that upfront).
            // If the property value is non-atomic, then it is a complex value, we might want to merge the new value comming from EPM with it.
            ODataProperty     existingProperty     = existingProperties.FirstOrDefault(p => string.CompareOrdinal(p.Name, propertyName) == 0);
            ODataComplexValue existingComplexValue = null;

            if (existingProperty != null)
            {
                // In case the property exists and it's a complex value we will try to merge.
                // Note that if the property is supposed to be complex, but it already has a null value, then the null wins.
                // Since in-content null complex value wins over any EPM complex value.
                existingComplexValue = existingProperty.Value as ODataComplexValue;
                if (existingComplexValue == null)
                {
                    return;
                }
            }

            IEdmProperty propertyMetadata = segmentStructuralTypeReference.FindProperty(propertyName);

            Debug.Assert(propertyMetadata != null || segmentStructuralTypeReference.IsOpen(), "We should have verified that if the property is not declared the type must be open.");

            if (propertyMetadata == null && propertyValuePathIndex != epmInfo.PropertyValuePath.Length - 1)
            {
                throw new ODataException(o.Strings.EpmReader_OpenComplexOrCollectionEpmProperty(epmInfo.Attribute.SourcePath));
            }

            // Open properties in EPM are by default of type Edm.String - there's no way to specify a typename in EPM
            // consumer is free to do the conversion later on if it needs to.
            // Note that this effectively means that ODataMessageReaderSettings.DisablePrimitiveTypeConversion is as if it's turned on for open EPM properties.
            IEdmTypeReference propertyType;

            if (propertyMetadata == null ||
                (this.MessageReaderSettings.DisablePrimitiveTypeConversion && propertyMetadata.Type.IsODataPrimitiveTypeKind()))
            {
                propertyType = EdmCoreModel.Instance.GetString(/*nullable*/ true);
            }
            else
            {
                propertyType = propertyMetadata.Type;
            }

            // NOTE: WCF DS Server only applies the values when
            // - It's an open property
            // - It's not a key property
            // - It's a key property and it's a POST operation
            // ODataLib here will always set the property though.
            switch (propertyType.TypeKind())
            {
            case EdmTypeKind.Primitive:
            {
                if (propertyType.IsStream())
                {
                    throw new ODataException(o.Strings.General_InternalError(InternalErrorCodes.EpmReader_SetEpmValueForSegment_StreamProperty));
                }

                object primitiveValue;
                if (propertyValue == null)
                {
                    ReaderValidationUtils.ValidateNullValue(this.atomInputContext.Model, propertyType, this.atomInputContext.MessageReaderSettings, /*validateNullValue*/ true, this.atomInputContext.Version);
                    primitiveValue = null;
                }
                else
                {
                    // Convert the value to the desired target type
                    primitiveValue = AtomValueUtils.ConvertStringToPrimitive((string)propertyValue, propertyType.AsPrimitive());
                }

                this.AddEpmPropertyValue(existingProperties, propertyName, primitiveValue, segmentStructuralTypeReference.IsODataEntityTypeKind());
            }

            break;

            case EdmTypeKind.Complex:
                // Note: Unlike WCF DS we don't have a preexisting instance to override (since complex values are atomic, so we should not updated them)
                // In our case the complex value either doesn't exist yet on the entry being reported (easy, create it)
                // or it exists, but then it was created during reading of previous normal or EPM properties for this entry. It never exists before
                // we ever get to see the entity. So in our case we will never recreate the complex value, we always start with new one
                // and update it with new properties as they come. (Next time we will start over with a new complex value.)
                Debug.Assert(
                    existingComplexValue == null || (existingProperty != null && existingProperty.Value == existingComplexValue),
                    "If we have existing complex value, we must have an existing property as well.");
                Debug.Assert(
                    epmInfo.PropertyValuePath.Length > propertyValuePathIndex + 1,
                    "Complex value can not be a leaf segment in the source property path. We should have failed constructing the EPM trees for it.");

                if (existingComplexValue == null)
                {
                    Debug.Assert(existingProperty == null, "If we don't have an existing complex value, then we must not have an existing property at all.");

                    // Create a new complex value and set its type name to the type name of the property type (in case of EPM we never have type name from the payload)
                    existingComplexValue = new ODataComplexValue
                    {
                        TypeName   = propertyType.ODataFullName(),
                        Properties = new ReadOnlyEnumerable <ODataProperty>()
                    };

                    this.AddEpmPropertyValue(existingProperties, propertyName, existingComplexValue, segmentStructuralTypeReference.IsODataEntityTypeKind());
                }

                // Get the properties list of the complex value and recursively set the next EPM segment value to it.
                // Note that on inner complex value we don't need to check for duplicate properties
                // because EPM will never add a property which already exists (see the start of this method).
                IEdmComplexTypeReference complexPropertyTypeReference = propertyType.AsComplex();
                Debug.Assert(complexPropertyTypeReference != null, "complexPropertyTypeReference != null");
                this.SetEpmValueForSegment(
                    epmInfo,
                    propertyValuePathIndex + 1,
                    complexPropertyTypeReference,
                    ReaderUtils.GetPropertiesList(existingComplexValue.Properties),
                    propertyValue);

                break;

            case EdmTypeKind.Collection:
                Debug.Assert(propertyType.IsNonEntityODataCollectionTypeKind(), "Collection types in EPM must be atomic.");

                // In this case the property value is the internal list of items.
                // Create a new collection value and set the list as the list of items on it.
                ODataCollectionValue collectionValue = new ODataCollectionValue
                {
                    TypeName = propertyType.ODataFullName(),
                    Items    = new ReadOnlyEnumerable((List <object>)propertyValue)
                };

                this.AddEpmPropertyValue(existingProperties, propertyName, collectionValue, segmentStructuralTypeReference.IsODataEntityTypeKind());

                break;

            default:
                throw new ODataException(o.Strings.General_InternalError(InternalErrorCodes.EpmReader_SetEpmValueForSegment_TypeKind));
            }
        }