/// <summary> /// Determines if the cache already contains a value for the specified EPM mapping. /// </summary> /// <param name="epmInfo">The EPM info for the EPM mapping to look for.</param> /// <returns>true if the cache already contains a value for this mapping, false otherwise.</returns> internal bool Contains(EntityPropertyMappingInfo epmInfo) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(epmInfo != null, "epmInfo != null"); return this.customEpmValues.Any(epmValue => object.ReferenceEquals(epmValue.Key, epmInfo)); }
/// <summary>Compares the defining type of this info and other EpmInfo object.</summary> /// <param name="other">The other EpmInfo object to compare to.</param> /// <returns>true if the defining types are the same</returns> internal bool DefiningTypesAreEqual(EntityPropertyMappingInfo other) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(other != null, "other != null"); return(this.DefiningType.IsEquivalentTo(other.DefiningType)); }
private static string GetPropertyNameFromEpmInfo(EntityPropertyMappingInfo epmInfo) { if (epmInfo.Attribute.TargetSyndicationItem == SyndicationItemProperty.CustomProperty) { return(epmInfo.Attribute.TargetPath); } return(epmInfo.Attribute.TargetSyndicationItem.ToString()); }
internal void Add(EntityPropertyMappingInfo epmInfo) { List <EpmSourcePathSegment> list = new List <EpmSourcePathSegment>(); EpmSourcePathSegment root = this.Root; EpmSourcePathSegment segment2 = null; IEdmType actualPropertyType = epmInfo.ActualPropertyType; string[] strArray = epmInfo.Attribute.SourcePath.Split(new char[] { '/' }); int length = strArray.Length; for (int i = 0; i < length; i++) { string propertyName = strArray[i]; if (propertyName.Length == 0) { throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_InvalidSourcePath(epmInfo.DefiningType.ODataFullName(), epmInfo.Attribute.SourcePath)); } IEdmTypeReference propertyType = GetPropertyType(actualPropertyType, propertyName); IEdmType type2 = (propertyType == null) ? null : propertyType.Definition; if ((type2 == null) && (i < (length - 1))) { throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_OpenComplexPropertyCannotBeMapped(propertyName, actualPropertyType.ODataFullName())); } actualPropertyType = type2; segment2 = root.SubProperties.SingleOrDefault <EpmSourcePathSegment>(e => e.PropertyName == propertyName); if (segment2 != null) { root = segment2; } else { EpmSourcePathSegment item = new EpmSourcePathSegment(propertyName); root.SubProperties.Add(item); root = item; } list.Add(root); } if ((actualPropertyType != null) && !actualPropertyType.IsODataPrimitiveTypeKind()) { throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_EndsWithNonPrimitiveType(root.PropertyName)); } if (segment2 != null) { if (segment2.EpmInfo.DefiningTypesAreEqual(epmInfo)) { throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_DuplicateEpmAttributesWithSameSourceName(epmInfo.DefiningType.ODataFullName(), epmInfo.Attribute.SourcePath)); } this.epmTargetTree.Remove(segment2.EpmInfo); } epmInfo.SetPropertyValuePath(list.ToArray()); root.EpmInfo = epmInfo; this.epmTargetTree.Add(epmInfo); }
/// <summary> /// Removes a path in the tree which is obtained by looking at the EntityPropertyMappingAttribute in the <paramref name="epmInfo"/>. /// </summary> /// <param name="epmInfo">EnitityPropertyMappingInfo holding the target path</param> internal void Remove(EntityPropertyMappingInfo epmInfo) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(epmInfo != null, "epmInfo != null"); string targetName = epmInfo.Attribute.TargetPath; string namespaceUri = epmInfo.Attribute.TargetNamespaceUri; EpmTargetPathSegment currentSegment = epmInfo.IsSyndicationMapping ? this.SyndicationRoot : this.NonSyndicationRoot; List <EpmTargetPathSegment> activeSubSegments = currentSegment.SubSegments; Debug.Assert(!string.IsNullOrEmpty(targetName), "Must have been validated during EntityPropertyMappingAttribute construction"); string[] targetSegments = targetName.Split('/'); for (int i = 0; i < targetSegments.Length; i++) { string targetSegment = targetSegments[i]; Debug.Assert(targetSegment.Length > 0 && (targetSegment[0] != '@' || i == targetSegments.Length - 1), "Target segments should have been checked when adding the path to the tree"); EpmTargetPathSegment foundSegment = activeSubSegments.FirstOrDefault( segment => segment.SegmentName == targetSegment && (epmInfo.IsSyndicationMapping || segment.SegmentNamespaceUri == namespaceUri)); if (foundSegment != null) { currentSegment = foundSegment; } else { return; } activeSubSegments = currentSegment.SubSegments; } // Recursively remove all the parent segments which will have no more children left // after removal of the current segment node if (currentSegment.EpmInfo != null) { // Since we are removing a property with KeepInContent false, we should decrement the count if (!currentSegment.EpmInfo.Attribute.KeepInContent) { this.countOfNonContentV2Mappings--; } EpmTargetPathSegment parentSegment = null; do { parentSegment = currentSegment.ParentSegment; parentSegment.SubSegments.Remove(currentSegment); currentSegment = parentSegment; }while (currentSegment.ParentSegment != null && !currentSegment.HasContent && currentSegment.SubSegments.Count == 0); } }
internal void Add(EntityPropertyMappingInfo epmInfo) { List<EpmSourcePathSegment> list = new List<EpmSourcePathSegment>(); EpmSourcePathSegment root = this.Root; EpmSourcePathSegment segment2 = null; IEdmType actualPropertyType = epmInfo.ActualPropertyType; string[] strArray = epmInfo.Attribute.SourcePath.Split(new char[] { '/' }); int length = strArray.Length; for (int i = 0; i < length; i++) { string propertyName = strArray[i]; if (propertyName.Length == 0) { throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_InvalidSourcePath(epmInfo.DefiningType.ODataFullName(), epmInfo.Attribute.SourcePath)); } IEdmTypeReference propertyType = GetPropertyType(actualPropertyType, propertyName); IEdmType type2 = (propertyType == null) ? null : propertyType.Definition; if ((type2 == null) && (i < (length - 1))) { throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_OpenComplexPropertyCannotBeMapped(propertyName, actualPropertyType.ODataFullName())); } actualPropertyType = type2; segment2 = root.SubProperties.SingleOrDefault<EpmSourcePathSegment>(e => e.PropertyName == propertyName); if (segment2 != null) { root = segment2; } else { EpmSourcePathSegment item = new EpmSourcePathSegment(propertyName); root.SubProperties.Add(item); root = item; } list.Add(root); } if ((actualPropertyType != null) && !actualPropertyType.IsODataPrimitiveTypeKind()) { throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_EndsWithNonPrimitiveType(root.PropertyName)); } if (segment2 != null) { if (segment2.EpmInfo.DefiningTypesAreEqual(epmInfo)) { throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_DuplicateEpmAttributesWithSameSourceName(epmInfo.DefiningType.ODataFullName(), epmInfo.Attribute.SourcePath)); } this.epmTargetTree.Remove(segment2.EpmInfo); } epmInfo.SetPropertyValuePath(list.ToArray()); root.EpmInfo = epmInfo; this.epmTargetTree.Add(epmInfo); }
/// <summary> /// Given an <see cref="EntityPropertyMappingInfo"/> gives the correct target path for it /// </summary> /// <param name="epmInfo">Given <see cref="EntityPropertyMappingInfo"/></param> /// <returns>string with the correct value for the target path</returns> private static string GetPropertyNameFromEpmInfo(EntityPropertyMappingInfo epmInfo) { if (epmInfo.Attribute.TargetSyndicationItem == SyndicationItemProperty.CustomProperty) { return(epmInfo.Attribute.TargetPath); } // for EF provider we want to return a name that corresponds to attribute in the edmx file while for CLR provider // and the client we want to return a name that corresponds to the enum value used in EntityPropertyMapping attribute. return (#if ASTORIA_SERVER epmInfo.IsEFProvider ? EpmTranslate.MapSyndicationPropertyToEpmTargetPath(epmInfo.Attribute.TargetSyndicationItem) : #endif epmInfo.Attribute.TargetSyndicationItem.ToString()); }
internal void Remove(EntityPropertyMappingInfo epmInfo) { string targetPath = epmInfo.Attribute.TargetPath; string namespaceUri = epmInfo.Attribute.TargetNamespaceUri; EpmTargetPathSegment item = epmInfo.IsSyndicationMapping ? this.SyndicationRoot : this.NonSyndicationRoot; List <EpmTargetPathSegment> subSegments = item.SubSegments; string[] strArray = targetPath.Split(new char[] { '/' }); for (int i = 0; i < strArray.Length; i++) { string targetSegment = strArray[i]; EpmTargetPathSegment segment2 = subSegments.FirstOrDefault <EpmTargetPathSegment>(delegate(EpmTargetPathSegment segment) { if (!(segment.SegmentName == targetSegment)) { return(false); } if (!epmInfo.IsSyndicationMapping) { return(segment.SegmentNamespaceUri == namespaceUri); } return(true); }); if (segment2 != null) { item = segment2; } else { return; } subSegments = item.SubSegments; } if (item.EpmInfo != null) { if (!item.EpmInfo.Attribute.KeepInContent) { this.countOfNonContentV2Mappings--; } EpmTargetPathSegment parentSegment = null; do { parentSegment = item.ParentSegment; parentSegment.SubSegments.Remove(item); item = parentSegment; }while (((item.ParentSegment != null) && !item.HasContent) && (item.SubSegments.Count == 0)); } }
/// <summary> /// Reads a property value starting on an entry. /// </summary> /// <param name="epmInfo">The EPM info which describes the mapping for which to read the property value.</param> /// <param name="epmValueCache">The EPM value cache for the entry to read from.</param> /// <param name="entityType">The type of the entry.</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> protected object ReadEntryPropertyValue( EntityPropertyMappingInfo epmInfo, EntryPropertiesValueCache epmValueCache, IEdmEntityTypeReference entityType) { Debug.Assert(epmInfo != null, "epmInfo != null"); Debug.Assert(epmInfo.PropertyValuePath != null, "The PropertyValuePath should have been initialized by now."); Debug.Assert(epmInfo.PropertyValuePath.Length > 0, "The PropertyValuePath must not be empty for an entry property."); Debug.Assert(entityType != null, "entityType != null"); // TODO - It might be possible to avoid the "value" type checks below if we do property value validation based on the type return this.ReadPropertyValue( epmInfo, epmValueCache.EntryProperties, 0, entityType, epmValueCache); }
internal void Add(EntityPropertyMappingInfo epmInfo) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(epmInfo != null, "epmInfo != null"); string targetPath = epmInfo.Attribute.TargetPath; string namespaceUri = epmInfo.Attribute.TargetNamespaceUri; string namespacePrefix = epmInfo.Attribute.TargetNamespacePrefix; EpmTargetPathSegment currentSegment = epmInfo.IsSyndicationMapping ? this.SyndicationRoot : this.NonSyndicationRoot; IList <EpmTargetPathSegment> activeSubSegments = currentSegment.SubSegments; Debug.Assert(!string.IsNullOrEmpty(targetPath), "Must have been validated during EntityPropertyMappingAttribute construction"); string[] targetSegments = targetPath.Split('/'); EpmTargetPathSegment foundSegment = null; for (int i = 0; i < targetSegments.Length; i++) { string targetSegment = targetSegments[i]; if (targetSegment.Length == 0) { throw new ODataException(Strings.EpmTargetTree_InvalidTargetPath_EmptySegment(targetPath)); } if (targetSegment[0] == '@' && i != targetSegments.Length - 1) { throw new ODataException(Strings.EpmTargetTree_AttributeInMiddle(targetSegment)); } foundSegment = activeSubSegments.SingleOrDefault( segment => segment.SegmentName == targetSegment && (epmInfo.IsSyndicationMapping || segment.SegmentNamespaceUri == namespaceUri)); if (foundSegment != null) { currentSegment = foundSegment; } else { currentSegment = new EpmTargetPathSegment(targetSegment, namespaceUri, namespacePrefix, currentSegment); if (targetSegment[0] == '@') { activeSubSegments.Insert(0, currentSegment); } else { activeSubSegments.Add(currentSegment); } } activeSubSegments = currentSegment.SubSegments; } if (currentSegment.EpmInfo != null) { // Two EpmAttributes with same TargetName in the inheritance hierarchy throw new ODataException(Strings.EpmTargetTree_DuplicateEpmAttributesWithSameTargetName(currentSegment.EpmInfo.DefiningType.ODataFullName(), EpmTargetTree.GetPropertyNameFromEpmInfo(currentSegment.EpmInfo), currentSegment.EpmInfo.Attribute.SourcePath, epmInfo.Attribute.SourcePath)); } // Increment the number of properties for which KeepInContent is false if (!epmInfo.Attribute.KeepInContent) { this.countOfNonContentV2Mappings++; } currentSegment.EpmInfo = epmInfo; // Mixed content is dis-allowed. List <EntityPropertyMappingAttribute> conflictingAttributes = new List <EntityPropertyMappingAttribute>(2); if (EpmTargetTree.HasMixedContent(this.NonSyndicationRoot, conflictingAttributes)) { Debug.Assert(conflictingAttributes.Count == 2, "Expected to find exactly two conflicting attributes."); throw new ODataException(Strings.EpmTargetTree_InvalidTargetPath_MixedContent(conflictingAttributes[0].TargetPath, conflictingAttributes[1].TargetPath)); } }
protected void SetEpmValue(IList targetList, IEdmTypeReference targetTypeReference, EntityPropertyMappingInfo epmInfo, object propertyValue) { this.SetEpmValueForSegment(epmInfo, 0, targetTypeReference.AsStructuredOrNull(), (List<ODataProperty>) targetList, propertyValue); }
/// <summary> /// Adds a value to cache. /// </summary> /// <param name="epmInfo">The EPM info for the mapping according to which the value was read.</param> /// <param name="value">The value to cache.</param> /// <remarks> /// The method will only store the first value for any given EPM info, since in custom EPM /// only the first occurrence of the element/attribute is used, the others are ignored. /// </remarks> internal void Add(EntityPropertyMappingInfo epmInfo, string value) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(epmInfo != null, "epmInfo != null"); Debug.Assert(!this.Contains(epmInfo), "We should not be trying to add more than one value for any given mapping."); this.customEpmValues.Add(new KeyValuePair<EntityPropertyMappingInfo, string>(epmInfo, value)); }
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)); } }
/// <summary> /// Adds a path to the source and target tree which is obtained by looking at the EntityPropertyMappingAttribute in the <paramref name="epmInfo"/> /// </summary> /// <param name="epmInfo">EnitityPropertyMappingInfo holding the source path</param> internal void Add(EntityPropertyMappingInfo epmInfo) { DebugUtils.CheckNoExternalCallers(); List <EpmSourcePathSegment> pathToCurrentSegment = new List <EpmSourcePathSegment>(); EpmSourcePathSegment currentSourceSegment = this.Root; EpmSourcePathSegment foundSourceSegment = null; IEdmType currentType = epmInfo.ActualPropertyType; Debug.Assert(!string.IsNullOrEmpty(epmInfo.Attribute.SourcePath), "Invalid source path"); string[] propertyPath = epmInfo.Attribute.SourcePath.Split('/'); int propertyPathLength = propertyPath.Length; Debug.Assert(propertyPathLength > 0, "Must have been validated during EntityPropertyMappingAttribute construction"); for (int sourcePropertyIndex = 0; sourcePropertyIndex < propertyPathLength; sourcePropertyIndex++) { string propertyName = propertyPath[sourcePropertyIndex]; if (propertyName.Length == 0) { throw new ODataException(ODataErrorStrings.EpmSourceTree_InvalidSourcePath(epmInfo.DefiningType.ODataFullName(), epmInfo.Attribute.SourcePath)); } IEdmTypeReference nextPropertyTypeReference = GetPropertyType(currentType, propertyName); IEdmType nextPropertyType = nextPropertyTypeReference == null ? null : nextPropertyTypeReference.Definition; // If we don't find a property type this is an open property; check whether this is the last segment in the path // since otherwise this would not be an open primitive property and only open primitive properties are allowed. if (nextPropertyType == null && sourcePropertyIndex < propertyPathLength - 1) { throw new ODataException(ODataErrorStrings.EpmSourceTree_OpenComplexPropertyCannotBeMapped(propertyName, currentType.ODataFullName())); } currentType = nextPropertyType; foundSourceSegment = currentSourceSegment.SubProperties.SingleOrDefault(e => e.PropertyName == propertyName); if (foundSourceSegment != null) { currentSourceSegment = foundSourceSegment; } else { EpmSourcePathSegment newSourceSegment = new EpmSourcePathSegment(propertyName); currentSourceSegment.SubProperties.Add(newSourceSegment); currentSourceSegment = newSourceSegment; } pathToCurrentSegment.Add(currentSourceSegment); } // The last segment is the one being mapped from by the user specified attribute. // It must be a primitive type - we don't allow mappings of anything else than primitive properties directly. // Note that we can only verify this for non-open properties, for open properties we must assume it's a primitive type // and we will make this check later during serialization again when we actually have the value of the property. if (currentType != null) { if (!currentType.IsODataPrimitiveTypeKind()) { throw new ODataException(ODataErrorStrings.EpmSourceTree_EndsWithNonPrimitiveType(currentSourceSegment.PropertyName)); } } Debug.Assert(foundSourceSegment == null || foundSourceSegment.EpmInfo != null, "Can't have a leaf node in the tree without EpmInfo."); // Two EpmAttributes with same PropertyName in the same type, this could be a result of inheritance if (foundSourceSegment != null) { Debug.Assert(object.ReferenceEquals(foundSourceSegment, currentSourceSegment), "currentSourceSegment variable should have been updated already to foundSourceSegment"); // Check for duplicates on the same entity type Debug.Assert(foundSourceSegment.SubProperties.Count == 0, "If non-leaf, it means we allowed complex type to be a leaf node"); if (foundSourceSegment.EpmInfo.DefiningTypesAreEqual(epmInfo)) { throw new ODataException(ODataErrorStrings.EpmSourceTree_DuplicateEpmAttributesWithSameSourceName(epmInfo.DefiningType.ODataFullName(), epmInfo.Attribute.SourcePath)); } // In case of inheritance, we need to remove the node from target tree which was mapped to base type property this.epmTargetTree.Remove(foundSourceSegment.EpmInfo); } epmInfo.SetPropertyValuePath(pathToCurrentSegment.ToArray()); currentSourceSegment.EpmInfo = epmInfo; this.epmTargetTree.Add(epmInfo); }
/// <summary> /// Sets the value read from EPM to a property on an entry. /// </summary> /// <param name="targetList">The target list, which is a list of properties (on entry or complex value).</param> /// <param name="targetTypeReference">The type of the value on which to set the property (can be entity or complex).</param> /// <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 SetEpmValue( IList targetList, IEdmTypeReference targetTypeReference, EntityPropertyMappingInfo epmInfo, object propertyValue) { Debug.Assert(epmInfo != null, "epmInfo != null"); Debug.Assert(targetTypeReference != null, "targetTypeReference != null"); Debug.Assert(targetList != null, "targetList != null"); Debug.Assert( targetTypeReference.IsODataEntityTypeKind() || targetTypeReference.IsODataComplexTypeKind(), "Only entity and complex types can have an EPM value set on them."); this.SetEpmValueForSegment( epmInfo, 0, targetTypeReference.AsStructuredOrNull(), (List<ODataProperty>)targetList, propertyValue); }
internal bool Contains(EntityPropertyMappingInfo epmInfo) { return this.customEpmValues.Any<KeyValuePair<EntityPropertyMappingInfo, string>>(epmValue => object.ReferenceEquals(epmValue.Key, epmInfo)); }
internal bool DefiningTypesAreEqual(EntityPropertyMappingInfo other) { return(((IEdmType)this.DefiningType).IsEquivalentTo(((IEdmType)other.DefiningType))); }
protected object ReadEntryPropertyValue(EntityPropertyMappingInfo epmInfo, EntryPropertiesValueCache epmValueCache, IEdmEntityTypeReference entityType) { return this.ReadPropertyValue(epmInfo, epmValueCache.EntryProperties, 0, entityType, 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); }
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)); } }
internal void Add(EntityPropertyMappingInfo epmInfo) { string targetPath = epmInfo.Attribute.TargetPath; string namespaceUri = epmInfo.Attribute.TargetNamespaceUri; string targetNamespacePrefix = epmInfo.Attribute.TargetNamespacePrefix; EpmTargetPathSegment parentSegment = epmInfo.IsSyndicationMapping ? this.SyndicationRoot : this.NonSyndicationRoot; IList <EpmTargetPathSegment> subSegments = parentSegment.SubSegments; string[] strArray = targetPath.Split(new char[] { '/' }); EpmTargetPathSegment segment2 = null; for (int i = 0; i < strArray.Length; i++) { string targetSegment = strArray[i]; if (targetSegment.Length == 0) { throw new ODataException(Microsoft.Data.OData.Strings.EpmTargetTree_InvalidTargetPath_EmptySegment(targetPath)); } if ((targetSegment[0] == '@') && (i != (strArray.Length - 1))) { throw new ODataException(Microsoft.Data.OData.Strings.EpmTargetTree_AttributeInMiddle(targetSegment)); } segment2 = subSegments.SingleOrDefault <EpmTargetPathSegment>(delegate(EpmTargetPathSegment segment) { if (!(segment.SegmentName == targetSegment)) { return(false); } if (!epmInfo.IsSyndicationMapping) { return(segment.SegmentNamespaceUri == namespaceUri); } return(true); }); if (segment2 != null) { parentSegment = segment2; } else { parentSegment = new EpmTargetPathSegment(targetSegment, namespaceUri, targetNamespacePrefix, parentSegment); if (targetSegment[0] == '@') { subSegments.Insert(0, parentSegment); } else { subSegments.Add(parentSegment); } } subSegments = parentSegment.SubSegments; } if (parentSegment.EpmInfo != null) { throw new ODataException(Microsoft.Data.OData.Strings.EpmTargetTree_DuplicateEpmAttributesWithSameTargetName(parentSegment.EpmInfo.DefiningType.ODataFullName(), GetPropertyNameFromEpmInfo(parentSegment.EpmInfo), parentSegment.EpmInfo.Attribute.SourcePath, epmInfo.Attribute.SourcePath)); } if (!epmInfo.Attribute.KeepInContent) { this.countOfNonContentV2Mappings++; } parentSegment.EpmInfo = epmInfo; List <EntityPropertyMappingAttribute> ancestorsWithContent = new List <EntityPropertyMappingAttribute>(2); if (HasMixedContent(this.NonSyndicationRoot, ancestorsWithContent)) { throw new ODataException(Microsoft.Data.OData.Strings.EpmTargetTree_InvalidTargetPath_MixedContent(ancestorsWithContent[0].TargetPath, ancestorsWithContent[1].TargetPath)); } }
/// <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); }
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); }
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); }
internal void Add(EntityPropertyMappingInfo epmInfo, string value) { this.customEpmValues.Add(new KeyValuePair<EntityPropertyMappingInfo, string>(epmInfo, value)); }