protected void ApplyProperty(ODataProperty property, ResourceType resourceType, object resource)
 {
     ResourceType type;
     string name = property.Name;
     ResourceProperty resourceProperty = resourceType.TryResolvePropertyName(name);
     if (resourceProperty == null)
     {
         type = null;
     }
     else
     {
         if (resourceProperty.Kind == ResourcePropertyKind.Stream)
         {
             return;
         }
         if (base.Update && resourceProperty.IsOfKind(ResourcePropertyKind.Key))
         {
             return;
         }
         type = resourceProperty.ResourceType;
     }
     object propertyValue = this.ConvertValue(property.Value, ref type);
     if (resourceProperty == null)
     {
         Deserializer.SetOpenPropertyValue(resource, name, propertyValue, base.Service);
     }
     else
     {
         Deserializer.SetPropertyValue(resourceProperty, resource, propertyValue, base.Service);
     }
 }
        /// <summary>
        /// Validates that a property with the specified name exists on a given resource type.
        /// The resource type can be null if no metadata is available.
        /// </summary>
        /// <param name="propertyName">The name of the property to validate.</param>
        /// <param name="owningType">The owning resource type of the property with name <paramref name="propertyName"/> 
        /// or null if no metadata is available.</param>
        /// <returns>The <see cref="ResourceProperty"/> instance representing the property with name <paramref name="propertyName"/> 
        /// or null if no metadata is available.</returns>
        internal static ResourceProperty ValidatePropertyDefined(string propertyName, ResourceType owningType)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(!string.IsNullOrEmpty(propertyName), "!string.IsNullOrEmpty(propertyName)");

            if (owningType == null)
            {
                return null;
            }

            ResourceProperty resourceProperty = owningType.TryResolvePropertyName(propertyName);

            // verify that the property is declared if the type is not an open type.
            if (!owningType.IsOpenType && resourceProperty == null)
            {
                throw new ODataException(Strings.ODataWriter_PropertyDoesNotExistOnType(propertyName, owningType.FullName));
            }

            return resourceProperty;
        }
        /// <summary>
        /// Constructs a ResourceAssociationEnd instance.
        /// </summary>
        /// <param name="resourceSet">Resource set of the association end.</param>
        /// <param name="resourceType">Resource type of the association end.</param>
        /// <param name="resourceProperty">Resource property of the association end.</param>
        public ResourceAssociationSetEnd(ResourceSet resourceSet, ResourceType resourceType, ResourceProperty resourceProperty)
        {
            WebUtil.CheckArgumentNull(resourceSet, "resourceSet");
            WebUtil.CheckArgumentNull(resourceType, "resourceType");

            if (resourceProperty != null && (resourceType.TryResolvePropertyName(resourceProperty.Name) == null || resourceProperty.TypeKind != ResourceTypeKind.EntityType))
            {
                throw new ArgumentException(Strings.ResourceAssociationSetEnd_ResourcePropertyMustBeNavigationPropertyOnResourceType);
            }

            if (!resourceSet.ResourceType.IsAssignableFrom(resourceType) && !resourceType.IsAssignableFrom(resourceSet.ResourceType))
            {
                throw new ArgumentException(Strings.ResourceAssociationSetEnd_ResourceTypeMustBeAssignableToResourceSet);
            }

            this.resourceSet = resourceSet;
            this.resourceType = resourceType;

            // Note that for the TargetEnd, resourceProperty can be null.
            this.resourceProperty = resourceProperty;
        }
        /// <summary>
        /// Constructs a ResourceAssociationEnd instance.
        /// </summary>
        /// <param name="resourceSet">Resource set of the association end.</param>
        /// <param name="resourceType">Resource type of the association end.</param>
        /// <param name="resourceProperty">Resource property of the association end.</param>
        public ResourceAssociationSetEnd(ResourceSet resourceSet, ResourceType resourceType, ResourceProperty resourceProperty)
        {
            WebUtil.CheckArgumentNull(resourceSet, "resourceSet");
            WebUtil.CheckArgumentNull(resourceType, "resourceType");

            if (resourceProperty != null && (resourceType.TryResolvePropertyName(resourceProperty.Name) == null || resourceProperty.TypeKind != ResourceTypeKind.EntityType))
            {
                throw new ArgumentException(Strings.ResourceAssociationSetEnd_ResourcePropertyMustBeNavigationPropertyOnResourceType);
            }

            if (!resourceSet.ResourceType.IsAssignableFrom(resourceType) && !resourceType.IsAssignableFrom(resourceSet.ResourceType))
            {
                throw new ArgumentException(Strings.ResourceAssociationSetEnd_ResourceTypeMustBeAssignableToResourceSet);
            }

            this.resourceSet  = resourceSet;
            this.resourceType = resourceType;

            // Note that for the TargetEnd, resourceProperty can be null.
            this.resourceProperty = resourceProperty;
        }
Beispiel #5
0
        public ResourceAssociationSet GetResourceAssociationSet(ResourceSetWrapper resourceSet, ResourceType resourceType, ResourceProperty resourceProperty)
        {
            ResourceAssociationSet set;

            resourceType = GetDeclaringTypeForProperty(resourceType, resourceProperty, null);
            string key = string.Concat(new object[] { resourceSet.Name, '_', resourceType.FullName, '_', resourceProperty.Name });

            if (!this.ResourceAssociationSetCache.TryGetValue(key, out set))
            {
                set = this.metadataProvider.GetResourceAssociationSet(resourceSet.ResourceSet, resourceType, resourceProperty);
                if (set != null)
                {
                    ResourceAssociationSetEnd end     = set.GetResourceAssociationSetEnd(resourceSet, resourceType, resourceProperty);
                    ResourceAssociationSetEnd end2    = set.GetRelatedResourceAssociationSetEnd(resourceSet, resourceType, resourceProperty);
                    ResourceSetWrapper        wrapper = this.ValidateResourceSet(end2.ResourceSet);
                    if (wrapper == null)
                    {
                        set = null;
                    }
                    else
                    {
                        ResourceType     type     = this.ValidateResourceType(end2.ResourceType);
                        ResourceProperty property = null;
                        if (end2.ResourceProperty != null)
                        {
                            ResourcePropertyKind stream = ResourcePropertyKind.Stream;
                            property = type.TryResolvePropertyName(end2.ResourceProperty.Name, stream);
                        }
                        resourceType = this.ValidateResourceType(end.ResourceType);
                        if ((((end.ResourceSet != resourceSet.ResourceSet) || (end.ResourceType != resourceType)) || ((end.ResourceProperty != resourceProperty) || (end2.ResourceSet != wrapper.ResourceSet))) || ((end2.ResourceType != type) || (end2.ResourceProperty != property)))
                        {
                            set = new ResourceAssociationSet(set.Name, new ResourceAssociationSetEnd(resourceSet.ResourceSet, resourceType, resourceProperty), new ResourceAssociationSetEnd(wrapper.ResourceSet, type, property));
                        }
                    }
                }
                this.ResourceAssociationSetCache.Add(key, set);
            }
            return(set);
        }
        /// <summary>
        /// Populate the properties of the given resource
        /// </summary>
        /// <param name="jsonObject">JsonObjectRecords containing property name and values</param>
        /// <param name="resource">instance of the resource whose properties needs to be populated</param>
        /// <param name="parentResourceSet">resource set where <paramref name="resource"/> belongs to</param>
        /// <param name="parentResourceType">resource type whose properties needs to be populated</param>
        /// <returns>true if any properties were set; false otherwise.</returns>
        private bool PopulateProperties(JsonReader.JsonObjectRecords jsonObject, object resource, ResourceSetWrapper parentResourceSet, ResourceType parentResourceType)
        {
            // Update all the properties specified in the payload. 
            // Don't touch the properties which are not specified. Its upto the provider to interpret
            // the meaning of things which are not specified
            bool changed = false;
            List<ResourceProperty> navProperties = new List<ResourceProperty>();
            List<object> navPropertyValues = new List<object>();

            #region Handle Non-Nav Properties
            foreach (string propertyName in jsonObject.OrderedKeys)
            {
                // Ignore the metadata property
                if (propertyName == XmlConstants.JsonMetadataString)
                {
                    continue;
                }

                // Check if the property exists and try and set the value
                ResourceProperty resourceProperty = parentResourceType.TryResolvePropertyName(propertyName);
                if (resourceProperty == null && parentResourceType.IsOpenType == false)
                {
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidPropertyNameSpecified(propertyName, parentResourceType.FullName));
                }

                // Get the property value, set it appropriately, and mark the object as changed.
                object propertyValue = jsonObject.Entries[propertyName];
                bool existingRelationship;

                // If its a open property
                if (resourceProperty == null)
                {
                    this.HandleOpenTypeProperties(resource, propertyName, propertyValue);
                    changed = true;
                }
                else if (resourceProperty.TypeKind == ResourceTypeKind.ComplexType)
                {
                    SegmentInfo segmentInfo = CreateSegment(resourceProperty, resourceProperty.Name, null, true /* singleResult */);
                    segmentInfo.TargetKind = RequestTargetKind.ComplexObject;
                    propertyValue = this.CreateObject(propertyValue, segmentInfo, false /*topLevel*/, out existingRelationship);
                    SetPropertyValue(resourceProperty, resource, propertyValue, ContentFormat.Json, this.Service);
                    changed = true;
                }
                else if (resourceProperty.TypeKind == ResourceTypeKind.Primitive)
                {
                    // Ignoring the value of key properties in PUT payload
                    if (!this.Update || !resourceProperty.IsOfKind(ResourcePropertyKind.Key))
                    {
                        SetPropertyValue(resourceProperty, resource, propertyValue, ContentFormat.Json, this.Service);
                    }

                    changed = true;
                }
                else
                {
                    Debug.Assert(ResourceTypeKind.EntityType == resourceProperty.TypeKind, "only expecting nav properties");

                    if (IsDeferredElement(propertyValue))
                    {
                        // Skip the deferred element
                        continue;
                    }
                    else
                    {
                        navProperties.Add(resourceProperty);
                        navPropertyValues.Add(propertyValue);
                    }
                }
            }

            #endregion Non-Navigation Properties

            #region Handle Navigation Properties

            Debug.Assert(navProperties.Count == navPropertyValues.Count, "nav properties and nav property values count must be the same");
            
            // The reason why we need to do this is so that we can gaurantee that the nav properties are getting set at the end.
            // This is nice, since we already do this in the atom deserializer. Hence its consistent. Second, we wanted to
            // give a gaurantee that when FK and nav properties are specified in the payload, nav properties always win.
            for (int i = 0; i < navProperties.Count; i++)
            {
                this.HandleNavigationProperty(parentResourceSet, parentResourceType, resource, navProperties[i], navPropertyValues[i]);
                changed = true;
            }            
            
            #endregion Handle Navigation Properties

            return changed;
        }
Beispiel #7
0
        /// <summary>
        /// Given a collection of <paramref name="segments"/> corresponding to a property access path
        /// on the <paramref name="element"/> object, sets the <paramref name="propertyValue"/> on the property
        /// </summary>
        /// <param name="segments">Property access path where each element is a property name</param>
        /// <param name="resourceType">Resource type for which to set the property</param>
        /// <param name="element">Object on which to set property</param>
        /// <param name="propertyValue">Value of property</param>
        /// <param name="currentIndex">Index of the current segment being looked at</param>
        /// <param name="deserializer">Current deserializer</param>
        internal void SetPropertyValueFromPath(
            String[] segments,
            ResourceType resourceType,
            object element,
            object propertyValue,
            int currentIndex,
            EpmContentDeSerializerBase deserializer)
        {
            String currentSegment = segments[currentIndex];

            ResourceProperty clientProp = resourceType.TryResolvePropertyName(currentSegment);
            ResourceType propertyType;
            if (clientProp == null && resourceType.IsOpenType == false)
            {
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidPropertyNameSpecified(currentSegment, resourceType.FullName));
            }

            // If this is a open property OR we do not have to do type conversion for primitive types,
            // read the type from the payload.
            if (clientProp == null ||
                (!deserializer.Service.Configuration.EnableTypeConversion && clientProp.ResourceType.ResourceTypeKind == ResourceTypeKind.Primitive))
            {
                String foundTypeName = deserializer.PropertiesApplied.MapPropertyToType(String.Join("/", segments, 0, currentIndex + 1));
                if (foundTypeName != null)
                {
                    propertyType = WebUtil.TryResolveResourceType(deserializer.Service.Provider, foundTypeName);
                    if (propertyType == null)
                    {
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeName(foundTypeName));
                    }

                    if (propertyType.ResourceTypeKind == ResourceTypeKind.EntityType)
                    {
                        throw DataServiceException.CreateBadRequestError(
                            Strings.PlainXml_EntityTypeNotSupported(propertyType.FullName));
                    }
                }
                else
                {
                    propertyType = ResourceType.PrimitiveStringResourceType;
                }
            }
            else
            {
                propertyType = clientProp.ResourceType;
            }

            object currentValue;

            // Re-construct the source path to add the newly applied property
            string sourcePath = string.Join("/", segments, 0, currentIndex + 1);

            switch (propertyType.ResourceTypeKind)
            {
                case ResourceTypeKind.ComplexType:
                    if (!deserializer.PropertiesApplied.Lookup(sourcePath))
                    {
                        // Complex types are treated as atomic and we never allow merging of properties belonging to
                        // a complex type.  In other words, we either update the whole complex type or not at all, 
                        // never just a subset of its properties.  If the complex property has not been applied yet
                        // we create a new instance then apply its property mappings.
                        currentValue = deserializer.Updatable.CreateResource(null, propertyType.FullName);
                        ResourceType.SetEpmProperty(element, currentSegment, currentValue, sourcePath, deserializer);
                    }
                    else
                    {
                        // We've already created a new instance of the complex property by now, reuse the same instance.
                        currentValue = deserializer.Updatable.GetValue(element, currentSegment);
                        Debug.Assert(currentValue != null, "currentValue != null -- we should never be here if the complex property were null.");
                    }

                    this.SetPropertyValueFromPath(segments, propertyType, currentValue, propertyValue, ++currentIndex, deserializer);
                    break;
                case ResourceTypeKind.EntityType:
                    throw DataServiceException.CreateBadRequestError(
                        Strings.PlainXml_NavigationPropertyNotSupported(clientProp.Name));
                default:
                    Debug.Assert(
                        propertyType.ResourceTypeKind == ResourceTypeKind.Primitive,
                        "property.TypeKind == ResourceTypeKind.Primitive -- metadata shouldn't return " + propertyType.ResourceTypeKind);

                    currentValue = PlainXmlDeserializer.ConvertValuesForXml(propertyValue, currentSegment, propertyType.InstanceType);

                    // Do not try to update the property if it is a key property
                    if (!deserializer.IsUpdateOperation || clientProp == null || !clientProp.IsOfKind(ResourcePropertyKind.Key))
                    {
                        ResourceType.SetEpmProperty(element, currentSegment, currentValue, sourcePath, deserializer);
                    }

                    break;
            }
        }
        /// <summary>
        /// Binds a key property value.
        /// </summary>
        /// <param name="namedValue">The named value to bind.</param>
        /// <param name="collectionItemType">The resource type of a single item in a collection to apply the key value to.</param>
        /// <returns>The bound key property value node.</returns>
        private KeyPropertyValue BindKeyPropertyValue(NamedValue namedValue, ResourceType collectionItemType)
        {
            // These are exception checks because the data comes directly from the potentially user specified tree.
            ExceptionUtils.CheckArgumentNotNull(namedValue, "namedValue");
            ExceptionUtils.CheckArgumentNotNull(namedValue.Value, "namedValue.Value");
            Debug.Assert(collectionItemType != null, "collectionItemType != null");
            Debug.Assert(collectionItemType.ResourceTypeKind == ResourceTypeKind.EntityType, "collectionItemType.ResourceTypeKind == ResourceTypeKind.EntityType");

            ResourceProperty keyProperty;
            if (namedValue.Name == null)
            {
                if (collectionItemType.KeyProperties.Count != 1)
                {
                    throw new ODataException(Strings.MetadataBinder_UnnamedKeyValueOnTypeWithMultipleKeyProperties(collectionItemType.FullName));
                }

                keyProperty = collectionItemType.KeyProperties[0];
            }
            else
            {
                keyProperty = collectionItemType.TryResolvePropertyName(namedValue.Name);
                if (keyProperty == null)
                {
                    throw new ODataException(Strings.MetadataBinder_PropertyNotDeclared(collectionItemType.FullName, namedValue.Name));
                }

                if (!keyProperty.IsOfKind(ResourcePropertyKind.Key))
                {
                    throw new ODataException(Strings.MetadataBinder_PropertyNotKeyInKeyValue(keyProperty.Name, collectionItemType.FullName));
                }
            }

            ResourceType keyPropertyType = this.metadataProvider.ValidateResourceType(keyProperty.ResourceType);

            SingleValueQueryNode value = (SingleValueQueryNode)this.Bind(namedValue.Value);

            // TODO: Check that the value is of primitive type
            value = this.ConvertToType(value, keyPropertyType);

            Debug.Assert(keyProperty != null, "keyProperty != null");
            return new KeyPropertyValue()
            {
                KeyProperty = keyProperty,
                KeyValue = value
            };
        }
        /// <summary>
        /// Returns a resource type of the property on the specified resource type.
        /// </summary>
        /// <param name="resourceType">The resource type to look for the property on.</param>
        /// <param name="propertyName">The name of the property to look for.</param>
        /// <param name="isMultiValueProperty">return true if the property was a MultiValue property.</param>
        /// <returns>The type of the property specified. Note that for MultiValue properties this returns the type of the item of the MultiValue property.</returns>
        private static ResourceType GetPropertyType(ResourceType resourceType, string propertyName, out bool isMultiValueProperty)
        {
            Debug.Assert(propertyName != null, "propertyName != null");

            isMultiValueProperty = false;
            ResourceProperty resourceProperty = resourceType != null ? resourceType.TryResolvePropertyName(propertyName) : null;

            if (resourceProperty != null)
            {
                if (resourceProperty.IsOfKind(ResourcePropertyKind.MultiValue))
                {
                    isMultiValueProperty = true;
                    Debug.Assert(resourceProperty.ResourceType is MultiValueResourceType, "MultiValue property must be of the MultiValueResourceType.");
                    MultiValueResourceType multiValueResourceType = (MultiValueResourceType)resourceProperty.ResourceType;
                    return multiValueResourceType.ItemType;
                }
                else
                {
                    return resourceProperty.ResourceType;
                }
            }
            else
            {
                if (resourceType != null && !resourceType.IsOpenType)
                {
                    // could be a named stream
                    resourceProperty = resourceType.TryResolveNamedStream(propertyName);
                    if (resourceProperty != null)
                    {
                        throw new ODataException(Strings.EpmSourceTree_NamedStreamCannotBeMapped(propertyName, resourceType.FullName));
                    }
                    else
                    {
                        throw new ODataException(Strings.EpmSourceTree_MissingPropertyOnType(propertyName, resourceType.FullName));
                    }
                }

                return null;
            }
        }
        /// <summary>Applies the information from a link to the specified resource.</summary>
        /// <param name='link'>LinkDescriptor with information to apply.</param>
        /// <param name="resourceSet">Set for the target resource.</param>
        /// <param name='resourceType'>Type for the target resource.</param>
        /// <param name='resource'>Target resource to which information will be applied.</param>
        /// <param name="propertyName">Name of the property that this link represents.</param>
        private void ApplyLink(SyndicationLink link, ResourceSetWrapper resourceSet, ResourceType resourceType, object resource, string propertyName)
        {
            Debug.Assert(link != null, "link != null");
            Debug.Assert(resourceType != null, "resourceType != null");
            Debug.Assert(resource != null, "resource != null");

            ResourceProperty property = resourceType.TryResolvePropertyName(propertyName);
            if (property == null)
            {
                // Open navigation properties are not supported on OpenTypes
                throw DataServiceException.CreateBadRequestError(Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(propertyName));
            }

            if (property.TypeKind != ResourceTypeKind.EntityType)
            {
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidNavigationPropertyName(propertyName, resourceType.FullName));
            }

            string typeParameterValue = ValidateTypeParameterForNonOpenTypeProperties(link.MediaType, property);
            LinkContent linkContent = this.HandleLinkContent(link, resource, resourceSet, resourceType, property, typeParameterValue, propertyName);

            #region Handle bind/unbind operation
            // If the href was specified empty or an empty inline element was specified, then we will set the 
            // reference to null - this helps in overrriding if there was a default non-value for this property
            // else if only link element was specified, and then href points to a single result, then we will
            // perform a bind operation
            if ((linkContent == LinkContent.NoInlineElementSpecified && link.Uri != null && String.IsNullOrEmpty(link.Uri.OriginalString)) ||
                linkContent == LinkContent.EmptyInlineElementSpecified)
            {
                // update the object count when you are performing a bind operation
                this.CheckAndIncrementObjectCount();
                if (property != null && property.Kind == ResourcePropertyKind.ResourceSetReference)
                {
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_CannotSetCollectionsToNull(propertyName));
                }

                // For open properties, we will assume that this is a reference property and set it to null
                this.Updatable.SetReference(resource, propertyName, null);
            }
            else if (linkContent == LinkContent.NoInlineElementSpecified && link.Uri != null && !String.IsNullOrEmpty(link.Uri.OriginalString))
            {
                // update the object count when you are performing a bind operation
                this.CheckAndIncrementObjectCount();

                // If the link points to a reference navigation property, then update the link
                Uri referencedUri = RequestUriProcessor.GetAbsoluteUriFromReference(link.Uri.OriginalString, this.Service.OperationContext);
                RequestDescription description = RequestUriProcessor.ProcessRequestUri(referencedUri, this.Service);
                if (!description.IsSingleResult)
                {
                    if (property != null && property.Kind == ResourcePropertyKind.ResourceReference)
                    {
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequest_LinkHrefMustReferToSingleResource(propertyName));
                    }

                    return;
                }

                // no need to check for null. For collection properties, they can never be null and that
                // check has been added below. For reference properties, if they are null, it means unbind
                // and hence no need to check for null.
                // Get the resource
                object targetResource = this.Service.GetResource(description, description.SegmentInfos.Length - 1, null);
                if (property.Kind == ResourcePropertyKind.ResourceReference)
                {
                    this.Updatable.SetReference(resource, propertyName, targetResource);
                }
                else
                {
                    WebUtil.CheckResourceExists(targetResource != null, description.LastSegmentInfo.Identifier);
                    this.Updatable.AddReferenceToCollection(resource, propertyName, targetResource);
                }
            }
            #endregion Handle bind/unbind operation
        }
        private void WriteObjectProperties(IExpandedResult expanded, object customObject, ResourceType resourceType, Uri parentUri, bool objectIsResource)
        {
            Debug.Assert(customObject != null, "customObject != null");
            Debug.Assert(resourceType != null, "customObjectType != null");
            Debug.Assert(
                ((customObject is IProjectedResult) && (resourceType.FullName == ((IProjectedResult)customObject).ResourceTypeName)) ||
                    (resourceType.InstanceType.IsAssignableFrom(customObject.GetType())),
                "The type of the object doesn't match the resource type specified.");
            Debug.Assert(parentUri != null, "parentUri != null");
            Debug.Assert(resourceType.ResourceTypeKind != ResourceTypeKind.Primitive, "resourceType.ResourceTypeKind == ResourceTypeKind.Primitive");

            // We should throw while if there are navigation properties in the derived entity type
            if (this.CurrentContainer != null && this.Provider.IsEntityTypeDisallowedForSet(this.CurrentContainer, resourceType))
            {
                throw new InvalidOperationException(Strings.BaseServiceProvider_NavigationPropertiesOnDerivedEntityTypesNotSupported(resourceType.FullName, this.CurrentContainer.Name));
            }

            IEnumerable<ProjectionNode> projectionNodes = null;
            if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType)
            {
                projectionNodes = this.GetProjections();
            }

            if (projectionNodes == null)
            {
                foreach (ResourceProperty property in this.Provider.GetResourceProperties(this.CurrentContainer, resourceType))
                {
                    Debug.Assert(
                        objectIsResource || !property.IsOfKind(ResourcePropertyKind.Key),
                        "objectIsResource || property.Kind != ResourcePropertyKind.KeyPrimitive - complex types shouldn't have key properties");

                    this.WriteObjectDeclaredProperty(expanded, customObject, property, parentUri);
                }

                if (resourceType.IsOpenType)
                {
                    foreach (var pair in this.Provider.GetOpenPropertyValues(customObject))
                    {
                        this.WriteObjectOpenProperty(pair.Key, pair.Value, parentUri);
                    }
                }
            }
            else
            {
                foreach (ProjectionNode projectionNode in projectionNodes)
                {
                    string propertyName = projectionNode.PropertyName;
                    ResourceProperty property = resourceType.TryResolvePropertyName(propertyName);

                    if (property != null)
                    {
                        if (property.TypeKind != ResourceTypeKind.EntityType ||
                            this.Provider.GetResourceProperties(this.CurrentContainer, resourceType).Contains(property))
                        {
                            this.WriteObjectDeclaredProperty(expanded, customObject, property, parentUri);
                        }
                    }
                    else
                    {
                        object propertyValue = WebUtil.GetPropertyValue(this.Provider, customObject, resourceType, null, propertyName);
                        this.WriteObjectOpenProperty(propertyName, propertyValue, parentUri);
                    }
                }
            }
        }
        /// <summary>Writes all the properties of the specified resource or complex object.</summary>
        /// <param name="expanded">Expanded properties for the result.</param>
        /// <param name="customObject">Resource or complex object with properties to write out.</param>
        /// <param name="resourceType">resourceType containing metadata about the current custom object</param>
        /// <param name="absoluteUri">absolute uri for the given resource</param>
        /// <param name="relativeUri">relative uri for the given resource</param>
        /// <param name="item">Item in which to place links / expansions.</param>
        /// <param name="content">Content in which to place values.</param>
        /// <param name="currentSourceRoot">Epm source sub-tree corresponding to <paramref name="customObject"/></param>
        private void WriteObjectProperties(IExpandedResult expanded, object customObject, ResourceType resourceType, Uri absoluteUri, string relativeUri, SyndicationItem item, DictionaryContent content, EpmSourcePathSegment currentSourceRoot)
        {
            Debug.Assert(customObject != null, "customObject != null");
            Debug.Assert(resourceType != null, "resourceType != null");

            Debug.Assert(!String.IsNullOrEmpty(relativeUri), "!String.IsNullOrEmpty(relativeUri)");
            
            if (absoluteUri == null && resourceType.ResourceTypeKind == ResourceTypeKind.EntityType)
            {
                // entity type should have an URI, complex type should not have an URI
                // If the static type of the object is "Object", we will mistreat an entity type as complex type and hit this situation
                throw new DataServiceException(500, Strings.BadProvider_InconsistentEntityOrComplexTypeUsage(resourceType.Name));
            }

            this.RecurseEnter();
            try
            {
                List<ResourcePropertyInfo> navProperties = null;
                IEnumerable<ProjectionNode> projectionNodes = null;
                if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType)
                {
                    Debug.Assert(this.CurrentContainer != null, "this.CurrentContainer != null");
                    if (this.Provider.IsEntityTypeDisallowedForSet(this.CurrentContainer, resourceType))
                    {
                        throw new InvalidOperationException(Strings.BaseServiceProvider_NavigationPropertiesOnDerivedEntityTypesNotSupported(resourceType.FullName, this.CurrentContainer.Name));
                    }

                    navProperties = new List<ResourcePropertyInfo>(resourceType.Properties.Count);

                    projectionNodes = this.GetProjections();
                }

                if (projectionNodes == null)
                {
                    var action = resourceType.DictionarySerializerDelegate;
                    if (action == null && this.Provider.IsV1Provider)
                    {
                        Module module = typeof(SyndicationSerializer).Module;
                        Type customObjectType = customObject.GetType();
                        Type[] parameterTypes = new Type[] { typeof(object), typeof(DictionaryContent) };
                        DynamicMethod method = new DynamicMethod("content_populator", typeof(void), parameterTypes, module, false /* skipVisibility */);
                        ILGenerator generator = method.GetILGenerator();
                        MethodInfo methodWritePrimitiveValue = typeof(SyndicationSerializer).GetMethod("WritePrimitiveValue", BindingFlags.Static | BindingFlags.NonPublic);

                        // Downcast the argument.
                        generator.Emit(OpCodes.Ldarg_0);
                        generator.Emit(OpCodes.Castclass, customObjectType);

                        foreach (ResourceProperty property in resourceType.Properties.Where(p => p.TypeKind == ResourceTypeKind.Primitive))
                        {
                            if (SyndicationSerializer.EpmNeedToSkip(currentSourceRoot, property.Name))
                            {
                                continue;
                            }

                            // WritePrimitiveValue(propertyValue, property.Name, property.ResourceType, content);
                            generator.Emit(OpCodes.Dup);
                            generator.Emit(OpCodes.Call, resourceType.GetPropertyInfo(property).GetGetMethod());
                            if (property.Type.IsValueType)
                            {
                                generator.Emit(OpCodes.Box, property.Type);
                            }

                            generator.Emit(OpCodes.Ldstr, property.Name);
                            generator.Emit(OpCodes.Ldstr, property.ResourceType.FullName);
                            generator.Emit(OpCodes.Ldarg_1);
                            generator.Emit(OpCodes.Call, methodWritePrimitiveValue);
                        }

                        generator.Emit(OpCodes.Pop);
                        generator.Emit(OpCodes.Ret);
                        action = (Action<object, DictionaryContent>)method.CreateDelegate(typeof(Action<object, DictionaryContent>), null);
                        resourceType.DictionarySerializerDelegate = action;
                    }

                    if (action != null)
                    {
                        action(customObject, content);
                    }
                    else
                    {
                        foreach (ResourceProperty property in resourceType.Properties.Where(p => p.TypeKind == ResourceTypeKind.Primitive))
                        {
                            object propertyValue = WebUtil.GetPropertyValue(this.Provider, customObject, resourceType, property, null);
                            if (SyndicationSerializer.EpmNeedToSkip(currentSourceRoot, property.Name))
                            {
                                continue;
                            }

                            WritePrimitiveValue(propertyValue, property.Name, property.ResourceType.FullName, content);
                        }
                    }

                    foreach (ResourceProperty property in this.Provider.GetResourceProperties(this.CurrentContainer, resourceType))
                    {
                        string propertyName = property.Name;
                        if (property.TypeKind == ResourceTypeKind.EntityType)
                        {
                            Debug.Assert(navProperties != null, "navProperties list must be assigned for entity types");

                            object propertyValue =
                                (this.ShouldExpandSegment(property.Name)) ? GetExpandedProperty(this.Provider, expanded, customObject, property) : null;
                            navProperties.Add(new ResourcePropertyInfo(property, propertyValue));
                        }
                        else
                        {
                            if (property.TypeKind == ResourceTypeKind.ComplexType)
                            {
                                object propertyValue = WebUtil.GetPropertyValue(this.Provider, customObject, resourceType, property, null);
                                bool needPop = this.PushSegmentForProperty(property);
                                this.WriteComplexObjectValue(
                                        propertyValue,
                                        propertyName,
                                        property.ResourceType,
                                        relativeUri + "/" + property.Name,
                                        content,
                                        SyndicationSerializer.EpmGetComplexPropertySegment(currentSourceRoot, property.Name));
                                this.PopSegmentName(needPop);
                            }
                        }
                    }

                    if (resourceType.IsOpenType)
                    {
                        IEnumerable<KeyValuePair<string, object>> properties = this.Provider.GetOpenPropertyValues(customObject);
                        foreach (KeyValuePair<string, object> property in properties)
                        {
                            string propertyName = property.Key;

                            if (String.IsNullOrEmpty(propertyName))
                            {
                                throw new DataServiceException(500, Strings.Syndication_InvalidOpenPropertyName(resourceType.FullName));
                            }

                            Type valueType;
                            ResourceType propertyResourceType;

                            object value = property.Value;

                            if (value == null || value == DBNull.Value)
                            {
                                valueType = typeof(string);
                                propertyResourceType = ResourceType.PrimitiveStringResourceType;
                            }
                            else
                            {
                                valueType = value.GetType();
                                propertyResourceType = WebUtil.GetResourceType(this.Provider, value);
                            }

                            // A null ResourceType indicates a ----ed type (eg, IntPtr or DateTimeOffset). So ignore it.
                            if (propertyResourceType == null)
                            {
                                throw new DataServiceException(500, Strings.Syndication_InvalidOpenPropertyType(propertyName));
                            }

                            if (propertyResourceType.ResourceTypeKind == ResourceTypeKind.Primitive)
                            {
                                if (value != null && SyndicationSerializer.EpmNeedToSkip(currentSourceRoot, propertyName))
                                {
                                    continue;
                                }

                                WritePrimitiveValue(value, propertyName, propertyResourceType.FullName, content);
                            }
                            else
                            {
                                if (propertyResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType)
                                {
                                    Debug.Assert(propertyResourceType.InstanceType == valueType, "propertyResourceType.Type == valueType");
                                    this.WriteComplexObjectValue(
                                            value,
                                            propertyName,
                                            propertyResourceType,
                                            relativeUri + "/" + propertyName,
                                            content,
                                            SyndicationSerializer.EpmGetComplexPropertySegment(currentSourceRoot, propertyName));
                                }
                                else
                                {
                                    Debug.Assert(
                                        propertyResourceType.ResourceTypeKind == ResourceTypeKind.EntityType,
                                        "propertyResourceType.ResourceTypeKind == ResourceTypeKind.EntityType -- otherwise should have been processed as primitve or complex type.");

                                    // Open navigation properties are not supported on OpenTypes
                                    throw DataServiceException.CreateBadRequestError(Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(propertyName));
                                }
                            }
                        }
                    }
                }
                else
                {
                    foreach (ProjectionNode projectionNode in projectionNodes)
                    {
                        string propertyName = projectionNode.PropertyName;
                        ResourceProperty property = resourceType.TryResolvePropertyName(propertyName);

                        // First solve the normal entity type property - turn it into a nav. property record
                        if (property != null && property.TypeKind == ResourceTypeKind.EntityType)
                        {
                            Debug.Assert(navProperties != null, "navProperties list must be assigned for entity types");

                            // By calling the GetResourceProperties we will use the cached list of properties
                            //   for the given type and set. But we have to search through it.
                            // We could use the GetContainer (since that's what the GetResourceProperties does) and check
                            //   if it returns null, but result of that is only partially cached so it might be expensive
                            //   to evaluate for each item in the feed.
                            if (this.Provider.GetResourceProperties(this.CurrentContainer, resourceType).Contains(property))
                            {
                                object expandedPropertyValue =
                                    (this.ShouldExpandSegment(propertyName)) ? GetExpandedProperty(this.Provider, expanded, customObject, property) : null;
                                navProperties.Add(new ResourcePropertyInfo(property, expandedPropertyValue));
                            }

                            continue;
                        }

                        // Now get the property value
                        object propertyValue = WebUtil.GetPropertyValue(this.Provider, customObject, resourceType, property, property == null ? propertyName : null);

                        // Determine the type of the property
                        ResourceType propertyResourceType;
                        if (property != null)
                        {
                            propertyResourceType = property.ResourceType;
                        }
                        else
                        {
                            if (propertyValue == null || propertyValue == DBNull.Value)
                            {
                                propertyResourceType = ResourceType.PrimitiveStringResourceType;
                            }
                            else
                            {
                                propertyResourceType = WebUtil.GetResourceType(this.Provider, propertyValue);

                                // A null ResourceType indicates a ----ed type (eg, IntPtr or DateTimeOffset). So ignore it.
                                if (propertyResourceType == null)
                                {
                                    throw new DataServiceException(500, Strings.Syndication_InvalidOpenPropertyType(propertyName));
                                }
                            }
                        }

                        // And write out the value (depending on the type of the property)
                        if (propertyResourceType.ResourceTypeKind == ResourceTypeKind.Primitive)
                        {
                            if (propertyValue == DBNull.Value)
                            {
                                propertyValue = null;
                            }

                            if (propertyValue != null && SyndicationSerializer.EpmNeedToSkip(currentSourceRoot, propertyName))
                            {
                                continue;
                            }

                            WritePrimitiveValue(propertyValue, propertyName, propertyResourceType.FullName, content);
                        }
                        else if (propertyResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType)
                        {
                            bool needPop = false;
                            if (property != null)
                            {
                                needPop = this.PushSegmentForProperty(property);
                            }

                            this.WriteComplexObjectValue(
                                    propertyValue,
                                    propertyName,
                                    propertyResourceType,
                                    relativeUri + "/" + propertyName,
                                    content,
                                    SyndicationSerializer.EpmGetComplexPropertySegment(currentSourceRoot, propertyName));
                            if (property != null)
                            {
                                this.PopSegmentName(needPop);
                            }
                        }
                        else
                        {
                            Debug.Assert(
                                propertyResourceType.ResourceTypeKind == ResourceTypeKind.EntityType,
                                "propertyResourceType.ResourceTypeKind == ResourceTypeKind.EntityType -- otherwise should have been processed as primitve or complex type.");

                            // Open navigation properties are not supported on OpenTypes
                            throw DataServiceException.CreateBadRequestError(Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(propertyName));
                        }
                    }
                }

                if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType)
                {
                    for (int i = 0; i < navProperties.Count; i++)
                    {
                        ResourcePropertyInfo propertyInfo = navProperties[i];
                        ResourceProperty navProperty = propertyInfo.Property;

                        Debug.Assert(
                            navProperty.IsOfKind(ResourcePropertyKind.ResourceReference) ||
                            navProperty.IsOfKind(ResourcePropertyKind.ResourceSetReference),
                            "this must be nav property");

                        // Generate a link - see http://tools.ietf.org/html/rfc4287#section-4.2.7
                        string linkType = navProperty.IsOfKind(ResourcePropertyKind.ResourceReference) ? XmlConstants.AtomEntryElementName : XmlConstants.AtomFeedElementName;
                        linkType = String.Format(CultureInfo.InvariantCulture, "{0};{1}={2}", XmlConstants.MimeApplicationAtom, XmlConstants.AtomTypeAttributeName, linkType);
                        string segmentIdentifier = navProperty.Name;

                        if (!this.ShouldExpandSegment(navProperty.Name))
                        {
                            WriteDeferredContentElement(
                                XmlConstants.DataWebRelatedNamespace + navProperty.Name,
                                navProperty.Name,
                                relativeUri + "/" + segmentIdentifier,
                                linkType,
                                item);
                        }
                        else
                        {
                            object propertyValue = propertyInfo.Value;
                            IExpandedResult expandedResultPropertyValue = propertyValue as IExpandedResult;
                            object expandedPropertyValue =
                                expandedResultPropertyValue != null ?
                                GetExpandedElement(expandedResultPropertyValue) :
                                propertyValue;
                            string propertyRelativeUri = relativeUri + "/" + segmentIdentifier;
                            Uri propertyAbsoluteUri = RequestUriProcessor.AppendUnescapedSegment(absoluteUri, segmentIdentifier);

                            SyndicationLink link = new SyndicationLink();
                            link.RelationshipType = XmlConstants.DataWebRelatedNamespace + navProperty.Name;
                            link.Title = navProperty.Name;
                            link.Uri = new Uri(propertyRelativeUri, UriKind.RelativeOrAbsolute);
                            link.MediaType = linkType;
                            item.Links.Add(link);

                            bool needPop = this.PushSegmentForProperty(navProperty);

                            // if this.CurrentContainer is null, the target set of the navigation property is hidden.
                            if (this.CurrentContainer != null)
                            {
                                if (navProperty.IsOfKind(ResourcePropertyKind.ResourceSetReference))
                                {
                                    IEnumerable enumerable;
                                    bool collection = WebUtil.IsElementIEnumerable(expandedPropertyValue, out enumerable);
                                    Debug.Assert(collection, "metadata loading must have ensured that navigation set properties must implement IEnumerable");

                                    SyndicationFeed feed = new SyndicationFeed();
                                    InlineAtomFeed inlineFeedExtension = new InlineAtomFeed(feed, this.factory);
                                    link.ElementExtensions.Add(inlineFeedExtension);
                                    IEnumerator enumerator = enumerable.GetEnumerator();
                                    try
                                    {
                                        bool hasMoved = enumerator.MoveNext();
                                        this.WriteFeedElements(
                                            propertyValue as IExpandedResult, 
                                            enumerator, 
                                            navProperty.ResourceType, 
                                            navProperty.Name, 
                                            propertyAbsoluteUri, 
                                            propertyRelativeUri, 
                                            hasMoved, 
                                            feed,
                                            true);
                                    }
                                    catch
                                    {
                                        WebUtil.Dispose(enumerator);
                                        throw;
                                    }
                                }
                                else
                                {
                                    SyndicationItem inlineItem = new SyndicationItem();
                                    this.WriteEntryElement(propertyValue as IExpandedResult, expandedPropertyValue, navProperty.ResourceType, propertyAbsoluteUri, propertyRelativeUri, inlineItem);
                                    InlineAtomItem inlineItemExtension = new InlineAtomItem(inlineItem, this.factory);
                                    link.ElementExtensions.Add(inlineItemExtension);
                                }
                            }

                            this.PopSegmentName(needPop);
                        }
                    }
                }
            }
            finally
            {
                // The matching call to RecurseLeave is in a try/finally block not because it's necessary in the 
                // presence of an exception (progress will halt anyway), but because it's easier to maintain in the 
                // code in the presence of multiple exit points (returns).
                this.RecurseLeave();
            }
        }
        /// <summary>Applies a property from the reader to the specified resource.</summary>
        /// <param name='reader'>XmlReader to read from.</param>
        /// <param name='propertyName'>Name of property to set on the specified resource.</param>
        /// <param name='resourceType'>Type of resource.</param>
        /// <param name='resource'>Resource to set value on.</param>
        private void ApplyProperty(XmlReader reader, string propertyName, ResourceType resourceType, object resource)
        {
            Debug.Assert(reader != null, "reader != null");
            Debug.Assert(propertyName != null, "propertyName != null");
            Debug.Assert(resourceType != null, "resourceType != null");
            Debug.Assert(resource != null, "resource != null");

            ResourceProperty property = resourceType.TryResolvePropertyName(propertyName);
            bool ignoreValue = false;
            if (property == null)
            {
                if (resourceType.IsOpenType == false)
                {
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidPropertyNameSpecified(propertyName, resourceType.FullName));
                }
            }
            else
            {
                if (this.Update && property.IsOfKind(ResourcePropertyKind.Key))
                {
                    ignoreValue = true;
                }
            }

            object propertyValue = this.ReadPropertyWithType(reader, propertyName, property);
            if (!ignoreValue)
            {
                if (property == null)
                {
                    Deserializer.SetOpenPropertyValue(resource, propertyName, propertyValue, this.Service);
                }
                else
                {
                    Deserializer.SetPropertyValue(property, resource, propertyValue, this.ContentFormat, this.Service);
                }
            }
        }
Beispiel #14
0
 private IEnumerable<ODataProperty> GetProjectedEntityProperties(object customObject, ResourceType currentResourceType, Uri relativeUri, IEnumerable<ProjectionNode> projectionNodesForCurrentResourceType)
 {
     List<ODataProperty> source = new List<ODataProperty>(currentResourceType.Properties.Count);
     foreach (ProjectionNode node in projectionNodesForCurrentResourceType)
     {
         string str = node.PropertyName;
         ResourceProperty property = node.TargetResourceType.TryResolvePropertyName(str);
         if (property != null)
         {
             if (property.TypeKind != ResourceTypeKind.EntityType)
             {
                 source.Add(this.GetODataPropertyForEntityProperty(customObject, currentResourceType, relativeUri, property));
             }
         }
         else
         {
             object propertyValue = WebUtil.GetPropertyValue(base.Provider, customObject, currentResourceType, null, str);
             source.Add(this.GetODataPropertyForOpenProperty(str, propertyValue));
         }
     }
     if (currentResourceType.HasEntityPropertyMappings)
     {
         foreach (EpmSourcePathSegment segment in currentResourceType.EpmSourceTree.Root.SubProperties)
         {
             string propertyName = segment.PropertyName;
             if (source.FirstOrDefault<ODataProperty>(p => (p.Name == propertyName)) == null)
             {
                 ResourcePropertyKind stream = ResourcePropertyKind.Stream;
                 ResourceProperty resourceProperty = currentResourceType.TryResolvePropertyName(propertyName, stream);
                 object obj3 = WebUtil.GetPropertyValue(base.Provider, customObject, currentResourceType, resourceProperty, (resourceProperty == null) ? propertyName : null);
                 if (resourceProperty != null)
                 {
                     ODataProperty item = new ODataProperty {
                         Name = propertyName,
                         Value = base.GetPropertyValue(propertyName, resourceProperty.ResourceType, obj3, resourceProperty == null)
                     };
                     source.Add(item);
                 }
                 else
                 {
                     source.Add(this.GetODataPropertyForOpenProperty(propertyName, obj3));
                 }
             }
         }
     }
     return source;
 }