Ejemplo n.º 1
0
 internal EntityPropertyMappingInfo(EntityPropertyMappingAttribute attribute, IEdmEntityType definingType, IEdmEntityType actualTypeDeclaringProperty)
 {
     this.attribute = attribute;
     this.definingType = definingType;
     this.actualPropertyType = actualTypeDeclaringProperty;
     this.isSyndicationMapping = this.attribute.TargetSyndicationItem != SyndicationItemProperty.CustomProperty;
 }
Ejemplo n.º 2
0
 public EntityPropertyMappingInfo(EntityPropertyMappingAttribute attribute, Type definingType, ClientTypeAnnotation actualPropertyType)
 {
     this.attribute = attribute;
     this.definingType = definingType;
     this.actualPropertyType = actualPropertyType;
     this.propertyValuePath = attribute.SourcePath.Split(new char[] { '/' });
     this.isSyndicationMapping = this.attribute.TargetSyndicationItem != SyndicationItemProperty.CustomProperty;
 }
Ejemplo n.º 3
0
 public EntityPropertyMappingInfo(EntityPropertyMappingAttribute attribute, ResourceType definingType, ResourceType actualPropertyType, bool isEFProvider)
 {
     this.isEFProvider = isEFProvider;
     this.attribute = attribute;
     this.definingType = definingType;
     this.actualPropertyType = actualPropertyType;
     this.propertyValuePath = attribute.SourcePath.Split(new char[] { '/' });
     this.isSyndicationMapping = this.attribute.TargetSyndicationItem != SyndicationItemProperty.CustomProperty;
 }
Ejemplo n.º 4
0
        /// <summary>
        /// Creates instance of EntityPropertyMappingInfo class.
        /// </summary>
        /// <param name="attribute">The <see cref="EntityPropertyMappingAttribute"/> corresponding to this object</param>
        /// <param name="definingType">Type the <see cref="EntityPropertyMappingAttribute"/> was defined on.</param>
        /// <param name="actualPropertyType">ClientType whose property is to be read.</param>
        public EntityPropertyMappingInfo(EntityPropertyMappingAttribute attribute, Type definingType, ClientType actualPropertyType)
        {
#endif
            Debug.Assert(attribute != null, "attribute != null");
            Debug.Assert(definingType != null, "definingType != null");
            Debug.Assert(actualPropertyType != null, "actualPropertyType != null");

            this.attribute = attribute;
            this.definingType = definingType;
            this.actualPropertyType = actualPropertyType;

            Debug.Assert(!string.IsNullOrEmpty(attribute.SourcePath), "Invalid source path");
            this.segmentedSourcePath = attribute.SourcePath.Split('/');
        }
Ejemplo n.º 5
0
        public EntityPropertyMappingInfo(EntityPropertyMappingAttribute attribute, Type definingType, ClientType actualPropertyType)
        {
#endif
            Debug.Assert(attribute != null, "attribute != null");
            Debug.Assert(definingType != null, "definingType != null");
            Debug.Assert(actualPropertyType != null, "actualPropertyType != null");

            this.attribute          = attribute;
            this.definingType       = definingType;
            this.actualPropertyType = actualPropertyType;

            Debug.Assert(!string.IsNullOrEmpty(attribute.SourcePath), "Invalid source path");
            this.segmentedSourcePath = attribute.SourcePath.Split('/');
        }
        /// <summary>
        /// Does given property in the attribute exist in the specified resource type.
        /// </summary>
        /// <param name="resourceType">The resource type to inspect.</param>
        /// <param name="epmAttribute">Attribute which has PropertyName.</param>
        /// <returns>true if property exists in the specified type, false otherwise.</returns>
        private static bool PropertyExistsOnType(ResourceType resourceType, EntityPropertyMappingAttribute epmAttribute)
        {
            Debug.Assert(resourceType != null, "resourceType != null");
            Debug.Assert(epmAttribute != null, "epmAttribute != null");

            int indexOfSeparator = epmAttribute.SourcePath.IndexOf('/');
            String propertyToLookFor = indexOfSeparator == -1 ? epmAttribute.SourcePath : epmAttribute.SourcePath.Substring(0, indexOfSeparator);
            return resourceType.PropertiesDeclaredOnThisType.Any(p => p.Name == propertyToLookFor);
        }
Ejemplo n.º 7
0
 /// <summary>
 /// Creates instance of EntityPropertyMappingInfo class.
 /// </summary>
 /// <param name="attribute">The <see cref="EntityPropertyMappingAttribute"/> corresponding to this object</param>
 /// <param name="definingType">Type the <see cref="EntityPropertyMappingAttribute"/> was defined on.</param>
 /// <param name="actualPropertyType">Type whose property is to be read. This can be different from defining type when inheritance is involved.</param>
 /// <param name="isEFProvider">Whether the current data source is an EF provider. Needed for error reporting.</param>
 public EntityPropertyMappingInfo(EntityPropertyMappingAttribute attribute, ResourceType definingType, ResourceType actualPropertyType, bool isEFProvider)
 {
     this.isEFProvider = isEFProvider;
 private static bool PropertyExistsOnType(IEdmStructuredType structuredType, EntityPropertyMappingAttribute epmAttribute)
 {
     int index = epmAttribute.SourcePath.IndexOf('/');
     string propertyToLookFor = (index == -1) ? epmAttribute.SourcePath : epmAttribute.SourcePath.Substring(0, index);
     return structuredType.DeclaredProperties.Any<IEdmProperty>(p => (p.Name == propertyToLookFor));
 }
Ejemplo n.º 9
0
		/// <summary> 
		/// Builds the EntityPropertyMappingInfo corresponding to an EntityPropertyMappingAttribute, also builds the delegate to
		/// be invoked in order to retrieve the property provided in the <paramref name="epmAttr"/> 
		/// </summary> 
		/// <param name="epmAttr">Source EntityPropertyMappingAttribute</param>
		/// <param name="definingType">Type that has the attribute applied to it</param> 
		/// <param name="isEFProvider">Is EF provider being initialized, used for error message formatting</param>
		internal void BuildEpmInfo(EntityPropertyMappingAttribute epmAttr, ResourceType definingType, bool isEFProvider)
		{
			// We don't need to check for null/empty status of the source path because it is already checked 
			// in the constructor for EntityPropertyMappingAttribute
//			ParameterExpression rsrcParam = Expression.Parameter(typeof(object), "rsrc");
//			ParameterExpression providerParam = Expression.Parameter(typeof(DataServiceProviderWrapper), "provider");
			ResourceProperty resourceProperty = null;

//			Expression propValReaderExpr = this.BuildPropertyReader(
//														rsrcParam,
//														providerParam,
//														this,
//														epmAttr.SourcePath.Split('/'),
//														0,
//														ref resourceProperty);
//			Delegate dlgPropValReader = Expression.Lambda(propValReaderExpr, rsrcParam, providerParam).Compile();
			// this.EpmSourceTree.Add(EntityPropertyMappingInfo epmAttr); // new EntityPropertyMappingInfo
//			                       	{
			                       		// Attribute = epmAttr, 
										//PropValReader = dlgPropValReader, 
										// DefiningType = definingType, 
										// IsEFProvider = isEFProvider
//			                       	});
		}
Ejemplo n.º 10
0
		/// <summary> 
		/// Adds an <see cref="EntityPropertyMappingAttribute"/> for the resource type.
		/// </summary> 
		/// <param name="attribute">Given <see cref="EntityPropertyMappingAttribute"/></param> 
		public void AddEntityPropertyMappingAttribute(EntityPropertyMappingAttribute attribute)
		{

			// EntityPropertyMapping attribute can not be added to readonly resource types.
			this.ThrowIfSealed();

			if (this.ResourceTypeKind != ResourceTypeKind.EntityType)
			{
				throw new InvalidOperationException("Not an entity type");
			}

			if (this.epmInfo == null)
			{
				this.epmInfo = new EpmInfoPerResourceType();
			}

			this.OwnEpmInfo.Add(attribute);
		}
Ejemplo n.º 11
0
		public void AddEntityPropertyMappingAttribute (EntityPropertyMappingAttribute attribute)
		{
			throw new NotImplementedException ();
		}
Ejemplo n.º 12
0
 /// <summary>
 /// Builds the EntityPropertyMappingInfo corresponding to an EntityPropertyMappingAttribute, also builds the delegate to
 /// be invoked in order to retrieve the property provided in the <paramref name="epmAttr"/>
 /// </summary>
 /// <param name="epmAttr">Source EntityPropertyMappingAttribute</param>
 /// <param name="definingType">Type that has the attribute applied to it</param>
 /// <param name="isEFProvider">Is EF provider being initialized, used for error message formatting</param>
 internal void BuildEpmInfo(EntityPropertyMappingAttribute epmAttr, ResourceType definingType, bool isEFProvider)
 {
     this.EpmSourceTree.Add(new EntityPropertyMappingInfo(epmAttr, definingType, this, isEFProvider));
 }
Ejemplo n.º 13
0
        /// <summary>Creates an EntityPropertyMappingAttribute and adds it to the <paramref name="typeDecl"/></summary>
        /// <param name="epmAttr">Attribute to add</param>
        /// <param name="typeDecl">Type declaration for which the attribute is generated</param>
        private void AddEpmAttributeToTypeDeclaration(EntityPropertyMappingAttribute epmAttr, CodeTypeDeclaration typeDecl)
        {
            if (epmAttr.TargetSyndicationItem != SyndicationItemProperty.CustomProperty)
            {
                var syndicationItem = new CodeFieldReferenceExpression(
                                        new CodeTypeReferenceExpression(typeof(SyndicationItemProperty)),
                                        epmAttr.TargetSyndicationItem.ToString());
                var contentKind = new CodeFieldReferenceExpression(
                                        new CodeTypeReferenceExpression(typeof(SyndicationTextContentKind)),
                                        epmAttr.TargetTextContentKind.ToString());

                CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(
                                                            TypeReference.FromString(
                                                                    Utils.WebFrameworkCommonNamespace + "." + "EntityPropertyMappingAttribute",
                                                                    true));
                AddAttributeArguments(attribute, new object[] { epmAttr.SourcePath, syndicationItem, contentKind, epmAttr.KeepInContent });
                typeDecl.CustomAttributes.Add(attribute);
            }
            else
            {
                CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(
                                                            TypeReference.FromString(
                                                                    Utils.WebFrameworkCommonNamespace + "." + "EntityPropertyMappingAttribute",
                                                                    true));
                AddAttributeArguments(attribute, new object[] { epmAttr.SourcePath, epmAttr.TargetPath, epmAttr.TargetNamespacePrefix, epmAttr.TargetNamespaceUri, epmAttr.KeepInContent });
                typeDecl.CustomAttributes.Add(attribute);
            }
        }
Ejemplo n.º 14
0
 /// <summary>
 /// Does given property in the attribute exist in this type or one of it's base types
 /// </summary>
 /// <param name="epmAttr">Attribute which has PropertyName</param>
 /// <returns>true if property exists in current type, false otherwise</returns>
 private bool PropertyExistsInCurrentType(EntityPropertyMappingAttribute epmAttr)
 {
     int indexOfSeparator = epmAttr.SourcePath.IndexOf('/');
     String propertyToLookFor = indexOfSeparator == -1 ? epmAttr.SourcePath : epmAttr.SourcePath.Substring(0, indexOfSeparator);
     return this.PropertiesDeclaredOnThisType.Any(p => p.Name == propertyToLookFor);
 }
Ejemplo n.º 15
0
        /// <summary>
        /// Given a resource type and its resource proeperty builds the EntityPropertyMappingInfo for the EntityPropertyMappingAttribute on it
        /// </summary>
        /// <param name="propertyInformation">EPM information for current property</param>
        /// <param name="entityProperty">Property for which to get the information</param>
        /// <param name="typeDecl">Type declaration to add the attributes to</param>
        private void EmitEpmAttributeForEntityProperty(
            EpmPropertyInformation propertyInformation, 
            EdmInfo entityProperty, 
            CodeTypeDeclaration typeDecl)
        {
            if (propertyInformation.IsAtom)
            {
                if (entityProperty.IsComplex)
                {
                    throw new InvalidOperationException(Strings.ObjectContext_SyndicationMappingForComplexPropertiesNotAllowed);
                }
                else
                {
                    EntityPropertyMappingAttribute epmAttr = new EntityPropertyMappingAttribute(
                                        propertyInformation.SourcePath,
                                        propertyInformation.SyndicationItem,
                                        propertyInformation.ContentKind,
                                        propertyInformation.KeepInContent);

                    this.AddEpmAttributeToTypeDeclaration(epmAttr, typeDecl);
                }
            }
            else
            {
                if (entityProperty.IsComplex)
                {
                    foreach (EntityPropertyMappingAttribute epmAttr in GetEpmAttrsFromComplexProperty(
                                                                        entityProperty.Member,
                                                                        propertyInformation.SourcePath,
                                                                        propertyInformation.TargetPath,
                                                                        propertyInformation.NsPrefix,
                                                                        propertyInformation.NsUri,
                                                                        propertyInformation.KeepInContent))
                    {
                        this.AddEpmAttributeToTypeDeclaration(epmAttr, typeDecl);
                    }
                }
                else
                {
                    EntityPropertyMappingAttribute epmAttr = new EntityPropertyMappingAttribute(
                                        propertyInformation.SourcePath,
                                        propertyInformation.TargetPath,
                                        propertyInformation.NsPrefix,
                                        propertyInformation.NsUri,
                                        propertyInformation.KeepInContent);

                    this.AddEpmAttributeToTypeDeclaration(epmAttr, typeDecl);
                }
            }
        }
		/// <summary>
		/// Creates instance of EntityPropertyMappingInfo class.
		/// </summary>
		/// <param name="attribute">The <see cref="EntityPropertyMappingAttribute"/> corresponding to this object</param>
		/// <param name="definingType">Type the <see cref="EntityPropertyMappingAttribute"/> was defined on.</param>
		/// <param name="actualTypeDeclaringProperty">Type whose property is to be read. This can be different from defining type when inheritance is involved.</param>
		internal EntityPropertyMappingInfo(EntityPropertyMappingAttribute attribute, ResourceType definingType, ResourceType actualTypeDeclaringProperty)
		{
			DebugUtils.CheckNoExternalCallers();
			Debug.Assert(attribute != null, "attribute != null");
			Debug.Assert(definingType != null, "definingType != null");
			Debug.Assert(actualTypeDeclaringProperty != null, "actualTypeDeclaringProperty != null");

			this.attribute = attribute;
			this.definingType = definingType;
			this.actualPropertyType = actualTypeDeclaringProperty;
			this.multiValueStatus = EntityPropertyMappingMultiValueStatus.None;

			this.CriteriaValue = attribute.CriteriaValue;

			// Infer the mapping type from the attribute
			this.isSyndicationMapping = this.attribute.TargetSyndicationItem != SyndicationItemProperty.CustomProperty;

			switch (this.attribute.TargetSyndicationItem)
			{
				case SyndicationItemProperty.AuthorEmail:
				case SyndicationItemProperty.AuthorName:
				case SyndicationItemProperty.AuthorUri:
					this.SyndicationParent = EpmSyndicationParent.Author;
					break;
				case SyndicationItemProperty.CategoryLabel:
				case SyndicationItemProperty.CategoryScheme:
				case SyndicationItemProperty.CategoryTerm:
					this.SyndicationParent = EpmSyndicationParent.Category;
					break;
				case SyndicationItemProperty.ContributorEmail:
				case SyndicationItemProperty.ContributorName:
				case SyndicationItemProperty.ContributorUri:
					this.SyndicationParent = EpmSyndicationParent.Contributor;
					break;
				case SyndicationItemProperty.LinkHref:
				case SyndicationItemProperty.LinkHrefLang:
				case SyndicationItemProperty.LinkLength:
				case SyndicationItemProperty.LinkRel:
				case SyndicationItemProperty.LinkTitle:
				case SyndicationItemProperty.LinkType:
					this.SyndicationParent = EpmSyndicationParent.Link;
					break;
				default:
					this.SyndicationParent = EpmSyndicationParent.Entry;
					break;
			}

			if (attribute.CriteriaValue != null)
			{
				if (this.SyndicationParent == EpmSyndicationParent.Category)
				{
					this.Criteria = EpmSyndicationCriteria.CategoryScheme;
				}
				else if (this.SyndicationParent == EpmSyndicationParent.Link)
				{
					this.Criteria = EpmSyndicationCriteria.LinkRel;
				}
				else 
				{
					this.Criteria = EpmSyndicationCriteria.None;
				}
			}
		}
Ejemplo n.º 17
0
 private static bool EpmPropertyExistsInDeclaredProperties(EntityPropertyMappingAttribute epmAttr, ReadOnlyCollection<ResourceProperty> declaredProperties)
 {
     int index = epmAttr.SourcePath.IndexOf('/');
     string propertyToLookFor = (index == -1) ? epmAttr.SourcePath : epmAttr.SourcePath.Substring(0, index);
     return declaredProperties.Any<ResourceProperty>(p => (p.Name == propertyToLookFor));
 }
Ejemplo n.º 18
0
 internal void AddEntityPropertyMappingAttributeInternal(EntityPropertyMappingAttribute attribute, bool isEFProvider)
 {
     WebUtil.CheckArgumentNull<EntityPropertyMappingAttribute>(attribute, "attribute");
     this.ThrowIfSealed();
     if (this.ResourceTypeKind != System.Data.Services.Providers.ResourceTypeKind.EntityType)
     {
         throw new InvalidOperationException(System.Data.Services.Strings.EpmOnlyAllowedOnEntityTypes(this.Name));
     }
     if (this.epmInfo == null)
     {
         this.epmInfo = new EpmInfoPerResourceType(isEFProvider);
     }
     this.OwnEpmInfo.Add(attribute);
 }
Ejemplo n.º 19
0
 public void AddEntityPropertyMappingAttribute(EntityPropertyMappingAttribute attribute)
 {
     this.AddEntityPropertyMappingAttributeInternal(attribute, false);
 }
Ejemplo n.º 20
0
        /// <summary>
        /// Adds an <see cref="EntityPropertyMappingAttribute"/> for the resource type.
        /// </summary>
        /// <param name="attribute">Given <see cref="EntityPropertyMappingAttribute"/></param>
        public void AddEntityPropertyMappingAttribute(EntityPropertyMappingAttribute attribute)
        {
            WebUtil.CheckArgumentNull(attribute, "attribute");

            // EntityPropertyMapping attribute can not be added to readonly resource types.
            this.ThrowIfSealed();

            if (this.ResourceTypeKind != ResourceTypeKind.EntityType)
            {
                throw new InvalidOperationException(Strings.EpmOnlyAllowedOnEntityTypes(this.Name));
            }

            if (this.epmInfo == null)
            {
                this.epmInfo = new EpmInfoPerResourceType();
            }

            this.OwnEpmInfo.Add(attribute);
        }
Ejemplo n.º 21
0
        /// <summary>
        /// Validates the annotation values parsed for an EPM mapping.
        /// </summary>
        /// <param name="annotationValues">The <see cref="EpmAnnotationValues"/> to validate.</param>
        /// <param name="typeName">The name of the type for which the annotations are validated or that declares the <paramref name="property"/>. Only used in error messages.</param>
        /// <param name="property">The property for which the annotations are validated; null if the annotations are for a type.</param>
        /// <returns>An <see cref="EntityPropertyMappingAttribute"/> instance that represents the mapping created from the <paramref name="annotationValues"/>.</returns>
        private static EntityPropertyMappingAttribute ValidateAnnotationValues(EpmAnnotationValues annotationValues, string typeName, IEdmProperty property)
        {
            Debug.Assert(annotationValues != null, "annotationValues != null");
            Debug.Assert(annotationValues.AttributeSuffix != null, "annotationValues.AttributeSuffix != null");
            Debug.Assert(!string.IsNullOrEmpty(typeName), "!string.IsNullOrEmpty(typeName)");

            //// Conditions for EPM annotation values to represent a valid mapping:
            ////   1. must have target path
            ////   2. can have keep-in-content (default is 'true')
            ////   3a. if custom mapping: target path must map to custom property, content kind must be null
            ////   3b. if syndication mapping: content kind (optional; default: plain text), no ns uri, no ns prefix

            if (annotationValues.TargetPath == null)
            {
                string attributeName = EpmConstants.ODataEpmTargetPath + annotationValues.AttributeSuffix;
                string errorMessage = property == null
                    ? o.Strings.EpmExtensionMethods_MissingAttributeOnType(attributeName, typeName)
                    : o.Strings.EpmExtensionMethods_MissingAttributeOnProperty(attributeName, property.Name, typeName);
                throw new ODataException(errorMessage);
            }

            EntityPropertyMappingAttribute mapping;

            bool keepInContent = true;
            if (annotationValues.KeepInContent != null)
            {
                if (!bool.TryParse(annotationValues.KeepInContent, out keepInContent))
                {
                    string attributeName = EpmConstants.ODataEpmKeepInContent + annotationValues.AttributeSuffix;
                    throw new InvalidOperationException(property == null 
                        ? o.Strings.EpmExtensionMethods_InvalidKeepInContentOnType(attributeName, typeName) 
                        : o.Strings.EpmExtensionMethods_InvalidKeepInContentOnProperty(attributeName, property.Name, typeName));
                }
            }

            // figure out whether this is a custom mapping or not
            SyndicationItemProperty targetSyndicationItem = MapTargetPathToSyndicationProperty(annotationValues.TargetPath);
            if (targetSyndicationItem == SyndicationItemProperty.CustomProperty)
            {
                if (annotationValues.ContentKind != null)
                {
                    string attributeName = EpmConstants.ODataEpmContentKind + annotationValues.AttributeSuffix;
                    string errorMessage = property == null
                        ? o.Strings.EpmExtensionMethods_AttributeNotAllowedForCustomMappingOnType(attributeName, typeName)
                        : o.Strings.EpmExtensionMethods_AttributeNotAllowedForCustomMappingOnProperty(attributeName, property.Name, typeName);
                    throw new ODataException(errorMessage);
                }

                mapping = new EntityPropertyMappingAttribute(
                    annotationValues.SourcePath,
                    annotationValues.TargetPath,
                    annotationValues.NamespacePrefix,
                    annotationValues.NamespaceUri,
                    keepInContent);
            }
            else
            {
                if (annotationValues.NamespaceUri != null)
                {
                    string attributeName = EpmConstants.ODataEpmNsUri + annotationValues.AttributeSuffix;
                    string errorMessage = property == null
                        ? o.Strings.EpmExtensionMethods_AttributeNotAllowedForAtomPubMappingOnType(attributeName, typeName)
                        : o.Strings.EpmExtensionMethods_AttributeNotAllowedForAtomPubMappingOnProperty(attributeName, property.Name, typeName);
                    throw new ODataException(errorMessage);
                }

                if (annotationValues.NamespacePrefix != null)
                {
                    string attributeName = EpmConstants.ODataEpmNsPrefix + annotationValues.AttributeSuffix;
                    string errorMessage = property == null
                        ? o.Strings.EpmExtensionMethods_AttributeNotAllowedForAtomPubMappingOnType(attributeName, typeName)
                        : o.Strings.EpmExtensionMethods_AttributeNotAllowedForAtomPubMappingOnProperty(attributeName, property.Name, typeName);
                    throw new ODataException(errorMessage);
                }

                SyndicationTextContentKind contentKind = SyndicationTextContentKind.Plaintext;
                if (annotationValues.ContentKind != null)
                {
                    contentKind = MapContentKindToSyndicationTextContentKind(
                        annotationValues.ContentKind, 
                        annotationValues.AttributeSuffix, 
                        typeName, 
                        property == null ? null : property.Name);
                }

                mapping = new EntityPropertyMappingAttribute(
                    annotationValues.SourcePath,
                    targetSyndicationItem,
                    contentKind,
                    keepInContent);
            }

            Debug.Assert(mapping != null, "mapping != null");
            return mapping;
        }
Ejemplo n.º 22
0
 public EntityPropertyMappingInfo(EntityPropertyMappingAttribute attribute, ResourceType definingType, ResourceType actualPropertyType, bool isEFProvider)
 {
     this.isEFProvider = isEFProvider;
Ejemplo n.º 23
0
        internal void Add(EntityPropertyMappingInfo epmInfo)
        {
            DebugUtils.CheckNoExternalCallers();

            List<EpmSourcePathSegment> pathToCurrentSegment = new List<EpmSourcePathSegment>();
            EpmSourcePathSegment currentSourceSegment = this.Root;
            EpmSourcePathSegment foundSourceSegment = null;
            ResourceType currentType = epmInfo.ActualPropertyType;
            EpmSourcePathSegment multiValuePropertySegment = null;

            Debug.Assert(!string.IsNullOrEmpty(epmInfo.Attribute.SourcePath), "Invalid source path");
            string[] propertyPath = epmInfo.Attribute.SourcePath.Split('/');

            if (epmInfo.CriteriaValue != null)
            {
                ValidateConditionalMapping(epmInfo);
            }

            Debug.Assert(propertyPath.Length > 0, "Must have been validated during EntityPropertyMappingAttribute construction");
            for (int sourcePropertyIndex = 0; sourcePropertyIndex < propertyPath.Length; sourcePropertyIndex++)
            {
                string propertyName = propertyPath[sourcePropertyIndex];

                if (propertyName.Length == 0)
                {
                    throw new ODataException(Strings.EpmSourceTree_InvalidSourcePath(epmInfo.DefiningType.Name, epmInfo.Attribute.SourcePath));
                }

                bool isMultiValueProperty;
                currentType = GetPropertyType(currentType, propertyName, out isMultiValueProperty);

                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);

                if (isMultiValueProperty)
                {
                    Debug.Assert(
                        currentSourceSegment.EpmInfo == null || currentSourceSegment.EpmInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.MultiValueProperty,
                        "MultiValue property must have EpmInfo marked as MultiValue or none at all.");
                    Debug.Assert(
                        currentSourceSegment.EpmInfo != null || foundSourceSegment == null,
                        "The only way to get a propety without info attached yet on a MultiValue property is when we just created it.");

                    if (multiValuePropertySegment != null)
                    {
                        // Nested MultiValue - not allowed to be mapped
                        throw new ODataException(Strings.EpmSourceTree_NestedMultiValue(
                            multiValuePropertySegment.EpmInfo.Attribute.SourcePath,
                            multiValuePropertySegment.EpmInfo.DefiningType.Name,
                            epmInfo.Attribute.SourcePath));
                    }

                    multiValuePropertySegment = currentSourceSegment;

                    // MultiValue properties can only be mapped to a top-level element, so we can blindly use the first part
                    //   of the target path as the target path for the MultiValue property.
                    Debug.Assert(!string.IsNullOrEmpty(epmInfo.Attribute.TargetPath), "Target path should have been checked by the EpmAttribute constructor.");
                    string multiValuePropertyTargetPath = epmInfo.Attribute.TargetPath.Split('/')[0];

                    if (currentSourceSegment.EpmInfo == null || !currentSourceSegment.EpmInfo.DefiningTypesAreEqual(epmInfo))
                    {
                        if (currentSourceSegment.EpmInfo != null)
                        {
                            Debug.Assert(!currentSourceSegment.EpmInfo.DefiningTypesAreEqual(epmInfo), "Just verifying that the ifs are correct.");
                            Debug.Assert(foundSourceSegment != null, "Can't have existing node with EpmInfo on it here and not found it before.");

                            // If the MultiValue property we're trying to add is from a different type than the one we already have
                            //   just overwrite the epm info. This means that the derived type defines a different mapping for the property than the base type.
                            //   We also need to walk all the children of the base type mapping here and remove them from the target tree
                            //   since we're overriding the entire MultiValue property mapping (not just one item property)
                            // Note that for MultiValue properties, removing the MultiValue property node itself will remove all the MultiValue item properties as well
                            //   as they have to be children of the MultiValue property node in the target tree.
                            this.epmTargetTree.Remove(foundSourceSegment.EpmInfo);

                            // We also need to remove all children of the MultiValue property node from the source tree
                            //   as the derived type is overriding it completely. If the derived type doesn't override all of the properties
                            //   we should fail as if it did't map all of them.
                            currentSourceSegment.SubProperties.Clear();
                        }

                        // This is the first time we've seen this MultiValue property mapped
                        //   (on this type, we might have seen it on the base type, but that has been removed)

                        // The source path is the path we have so far for the property
                        string multiValuePropertySourcePath = string.Join("/", propertyPath, 0, sourcePropertyIndex + 1);

                        if (!epmInfo.IsSyndicationMapping)
                        {
                            // Custom EPM for MultiValue is not supported yet
                            // Note: This has already been implemented, but then removed from the code. To see what it takes to implement this
                            //   please see the change which adds this comment into the sources.
                            throw new ODataException(Strings.EpmSourceTree_MultiValueNotAllowedInCustomMapping(
                                multiValuePropertySourcePath,
                                epmInfo.DefiningType.Name));
                        }

                        // Create a new EPM attribute to represent the MultiValue property mapping
                        //   note that this attribute is basically implicitly declared whenever the user declares EPM attribute
                        //   for a property from some MultiValue property. (the declaration happens right here)
                        EntityPropertyMappingAttribute multiValueEpmAttribute = new EntityPropertyMappingAttribute(
                                multiValuePropertySourcePath,
                                multiValuePropertyTargetPath,
                                epmInfo.Attribute.TargetNamespacePrefix,
                                epmInfo.Attribute.TargetNamespaceUri,
                                epmInfo.Attribute.KeepInContent);

                        // Create a special EpmInfo from the above special attribute which represents just the MultiValue property itself
                        EntityPropertyMappingInfo multiValueEpmInfo = new EntityPropertyMappingInfo(
                            multiValueEpmAttribute, 
                            epmInfo.DefiningType, 
                            epmInfo.ActualPropertyType);
                        multiValueEpmInfo.MultiValueStatus = EntityPropertyMappingMultiValueStatus.MultiValueProperty;
                        multiValueEpmInfo.MultiValueItemType = currentType;

                        // We need to mark the info as syndication/custom mapping explicitely since the attribute we create (From which it's infered) is always custom mapping
                        Debug.Assert(epmInfo.IsSyndicationMapping, "Only syndication mapping is allowed for MultiValue properties.");
                        multiValueEpmInfo.SetMultiValuePropertySyndicationMapping();

                        multiValueEpmInfo.SetPropertyValuePath(pathToCurrentSegment.ToArray());

                        multiValueEpmInfo.Criteria = epmInfo.Criteria;
                        multiValueEpmInfo.CriteriaValue = epmInfo.CriteriaValue;

                        // Now associate the current source tree segment with the new info object (or override the one from base)
                        currentSourceSegment.EpmInfo = multiValueEpmInfo;

                        // And add the new info to the target tree
                        this.epmTargetTree.Add(multiValueEpmInfo);

                        // And continue with the walk of the source path.
                        // This means that the EPM attribute specified as the input to this method is still to be added
                        // It might be added to the source tree if the path is longer (property on an item in the MultiValue property of complex types)
                        //   or it might not be added to the source tree if this segment is the last (MultiValue property of primitive types).
                        // In any case it will be added to the target tree (so even if the MultiValue property itself is mapped to the top-level element only
                        //   the items in the MultiValue property can be mapped to child element/attribute of that top-level element).
                    }
                    else
                    {
                        Debug.Assert(currentSourceSegment.EpmInfo.DefiningTypesAreEqual(epmInfo), "The condition in the surrounding if is broken.");

                        // We have already found a MultiValue property mapped from this source node.
                        // If it's on the same defining type we need to make sure that it's the same MultiValue property being mapped
                        // First verify that the mapping for the other property has the same top-level element for the MultiValue property
                        //   since we only allow properties from one MultiValue property to be mapped to the same top-level element
                        if (multiValuePropertyTargetPath != currentSourceSegment.EpmInfo.Attribute.TargetPath ||
                            epmInfo.Attribute.TargetNamespacePrefix != currentSourceSegment.EpmInfo.Attribute.TargetNamespacePrefix ||
                            epmInfo.Attribute.TargetNamespaceUri != currentSourceSegment.EpmInfo.Attribute.TargetNamespaceUri ||
                            epmInfo.Criteria != currentSourceSegment.EpmInfo.Criteria ||
                            String.Compare(epmInfo.Attribute.CriteriaValue, currentSourceSegment.EpmInfo.CriteriaValue, StringComparison.OrdinalIgnoreCase) != 0)
                        {
                            throw new ODataException(Strings.EpmSourceTree_PropertiesFromSameMultiValueMappedToDifferentTopLevelElements(currentSourceSegment.EpmInfo.Attribute.SourcePath, currentSourceSegment.EpmInfo.DefiningType.Name));
                        }

                        // Second verify that the mappings for both properties have the same KeepInContent value
                        if (epmInfo.Attribute.KeepInContent != currentSourceSegment.EpmInfo.Attribute.KeepInContent)
                        {
                            throw new ODataException(Strings.EpmSourceTree_PropertiesFromSameMultiValueMappedWithDifferentKeepInContent(currentSourceSegment.EpmInfo.Attribute.SourcePath, currentSourceSegment.EpmInfo.DefiningType.Name));
                        }
                    }
                }
            }

            // 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.ResourceTypeKind != ResourceTypeKind.Primitive)
                {
                    throw new ODataException(Strings.EpmSourceTree_EndsWithNonPrimitiveType(currentSourceSegment.PropertyName));
                }

                SyndicationItemProperty targetSyndicationItem = epmInfo.Attribute.TargetSyndicationItem;
                if (targetSyndicationItem == SyndicationItemProperty.LinkRel || targetSyndicationItem == SyndicationItemProperty.CategoryScheme)
                {
                    if (PrimitiveStringResourceType != currentType)
                    {
                        throw new InvalidOperationException(Strings.EpmSourceTree_NonStringPropertyMappedToConditionAttribute(
                            currentSourceSegment.PropertyName,
                            epmInfo.DefiningType.FullName,
                            targetSyndicationItem.ToString()));
                    }
                }
            }

            if (multiValuePropertySegment == currentSourceSegment)
            {
                // If the MultiValue property is the last segment it means that the MultiValue property itself is being mapped (and it must be a MultiValue of primitive types).

                // If we found the MultiValue property already in the tree, here we actually want the item of the MultiValue property (as the MultiValue one was processed above already)
                // If we have the item value already in the tree use it as the foundProperty so that we correctly check the duplicate mappings below
                if (foundSourceSegment != null)
                {
                    Debug.Assert(foundSourceSegment == currentSourceSegment, "If we found an existing segment it must be the current one.");
                    foundSourceSegment = currentSourceSegment.SubProperties.SingleOrDefault(e => e.IsMultiValueItemValue);
                }

                if (foundSourceSegment == null)
                {
                    // This is a bit of a special case. In the source tree we will create a special node to represent the item value (we need that to be able to tell
                    //   if it was not mapped twice).
                    // In the target tree, we will also create a special node which will hold the information specific
                    //   to serialization of the item value (for example the exact syndication mapping target and so on).
                    //   The creation of the special node is done in the target tree Add method.
                    EpmSourcePathSegment newSegment = EpmSourcePathSegment.CreateMultiValueItemValueSegment();
                    currentSourceSegment.SubProperties.Add(newSegment);
                    currentSourceSegment = newSegment;
                }
                else
                {
                    currentSourceSegment = foundSourceSegment;
                }
            }

            // Note that once we're here the EpmInfo we have is never the MultiValue property itself, it's always either a non-MultiValue property
            //   or MultiValue item property.
            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 ResourceType, 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");
                Debug.Assert(
                    foundSourceSegment.EpmInfo.MultiValueStatus != EntityPropertyMappingMultiValueStatus.MultiValueProperty,
                    "We should never get here with a MultiValue property itself, we should have a node represent its item or property on the item instead.");

                // 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(Strings.EpmSourceTree_DuplicateEpmAttrsWithSameSourceName(epmInfo.Attribute.SourcePath, epmInfo.DefiningType.Name));
                }

                // 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;

            if (multiValuePropertySegment != null)
            {
                Debug.Assert(multiValuePropertySegment.EpmInfo != null, "All MultiValue property segments must have EpmInfo assigned.");

                // We are mapping a MultiValue property - so mark the info as a MultiValue item property (since the MultiValue property itself was added above)
                epmInfo.MultiValueStatus = EntityPropertyMappingMultiValueStatus.MultiValueItemProperty;

                // Set the item type on each of the item properties, so that the segmented path know from which type to start
                epmInfo.MultiValueItemType = multiValuePropertySegment.EpmInfo.MultiValueItemType;

                // And trim its property value path to start from the MultiValue item. This path is basically a list of properties to traverse
                //   when access the value of the property on the specified resource. For non-MultiValue and MultiValue properties themselves 
                //   this path starts with the entity instance. For MultiValue item properties this path starts with the MultiValue item instance.
                //   Note that if it's a MultiValue of primitive types, the path is going to be empty meaning that the value is the item instance itself.
                epmInfo.TrimMultiValueItemPropertyPath(multiValuePropertySegment.EpmInfo);

#if DEBUG
                // Check that if the MultiValue item is of primitive type, we can only ever add a single child source segment which points directly to the MultiValue property itself
                // If we would allow this here, we would fail later, but with a much weirder error message
                Debug.Assert(
                    multiValuePropertySegment.EpmInfo.MultiValueItemType.ResourceTypeKind != ResourceTypeKind.Primitive || epmInfo.PropertyValuePath.Length == 0,
                    "We shoud have failed to map a subproperty of a primitive MultiValue item.");
#endif
            }

            this.epmTargetTree.Add(epmInfo);
        }
Ejemplo n.º 24
0
 private static void BuildEpmInfo(EntityPropertyMappingAttribute epmAttr, Type definingType, ClientTypeAnnotation clientTypeAnnotation, System.Data.Services.Client.Serializers.EpmSourceTree sourceTree)
 {
     sourceTree.Add(new System.Data.Services.Client.Serializers.EntityPropertyMappingInfo(epmAttr, definingType, clientTypeAnnotation));
 }
Ejemplo n.º 25
0
		/// <summary>
		/// Adds an <see cref="EntityPropertyMappingAttribute"/> for the resource type.
		/// </summary>
		/// <param name="attribute">Given <see cref="EntityPropertyMappingAttribute"/>.</param>
		public void AddEntityPropertyMappingAttribute(EntityPropertyMappingAttribute attribute)
		{
			ExceptionUtils.CheckArgumentNotNull(attribute, "attribute");

			// EntityPropertyMapping attribute can not be added to readonly resource types.
			this.ThrowIfSealed();

			if (this.ResourceTypeKind != ResourceTypeKind.EntityType)
			{
				throw new InvalidOperationException(Strings.ResourceType_EpmOnlyAllowedOnEntityTypes(this.Name));
			}

			// Initialize the EPM annotation for this type
			EpmResourceTypeAnnotation epm = this.Epm();
			if (epm == null)
			{
				epm = new EpmResourceTypeAnnotation();
				this.SetAnnotation(epm);
			}

			// And add the attribute to it
			epm.OwnEpmAttributes.Add(attribute);
		}