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