示例#1
0
        /// <summary>Gets ODataProperty for the given <paramref name="property"/>.</summary>
        /// <param name="entityToSerialize">Entity that is currently being serialized.</param>
        /// <param name="property">ResourceProperty instance in question.</param>
        /// <returns>A instance of ODataProperty for the given <paramref name="property"/>.</returns>
        private ODataProperty GetODataPropertyForEntityProperty(EntityToSerialize entityToSerialize, ResourceProperty property)
        {
            Debug.Assert(entityToSerialize != null, "entityToSerialize != null");
            Debug.Assert(property != null && entityToSerialize.ResourceType.Properties.Contains(property), "property != null && currentResourceType.Properties.Contains(property)");

            ODataValue odataPropertyValue;

            if (property.IsOfKind(ResourcePropertyKind.Stream))
            {
                odataPropertyValue = this.GetNamedStreamPropertyValue(entityToSerialize, property);
            }
            else
            {
                object propertyValue = WebUtil.GetPropertyValue(this.Provider, entityToSerialize.Entity, entityToSerialize.ResourceType, property, null);
                odataPropertyValue = this.GetPropertyValue(property.Name, property.ResourceType, propertyValue, false /*openProperty*/);
            }

            ODataProperty odataProperty = new ODataProperty()
            {
                Name  = property.Name,
                Value = odataPropertyValue
            };

            ODataPropertyKind kind = property.IsOfKind(ResourcePropertyKind.Key) ? ODataPropertyKind.Key : property.IsOfKind(ResourcePropertyKind.ETag) ? ODataPropertyKind.ETag : ODataPropertyKind.Unspecified;

            if (kind != ODataPropertyKind.Unspecified)
            {
                odataProperty.SetSerializationInfo(new ODataPropertySerializationInfo {
                    PropertyKind = kind
                });
            }

            return(odataProperty);
        }
        public OperationLinkBuilderTests()
        {
            ResourceType intType = ResourceType.GetPrimitiveResourceType(typeof(int));

            var customerType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "FQ.NS", "Customer", false);
            customerType.CanReflectOnInstanceType = false;
            customerType.AddProperty(new ResourceProperty("Id", ResourcePropertyKind.Primitive | ResourcePropertyKind.Key, intType) { CanReflectOnInstanceTypeProperty = false });
            customerType.SetReadOnly();

            var operation = new ServiceAction("Action", intType, OperationParameterBindingKind.Sometimes, new[] { new ServiceActionParameter("P1", customerType), new ServiceActionParameter("P2", intType) }, null);
            operation.SetReadOnly();
            this.operationWithParameters = new OperationWrapper(operation);

            var typeWithEscapedName = new ResourceType(typeof(object), ResourceTypeKind.ComplexType, null, "FQ NS", "+ /", false);
            typeWithEscapedName.CanReflectOnInstanceType = false;
            typeWithEscapedName.AddProperty(new ResourceProperty("Number", ResourcePropertyKind.Primitive, intType) { CanReflectOnInstanceTypeProperty = false });
            typeWithEscapedName.SetReadOnly();

            operation = new ServiceAction("Action", intType, OperationParameterBindingKind.Sometimes, new[] { new ServiceActionParameter("P1", customerType), new ServiceActionParameter("P2", typeWithEscapedName) }, null);
            operation.SetReadOnly();
            this.operationWithEscapedParameter = new OperationWrapper(operation);

            var bestCustomerType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, customerType, "FQ.NS", "BestCustomer", false);
            bestCustomerType.SetReadOnly();

            operation = new ServiceAction("Action", intType, OperationParameterBindingKind.Sometimes, new[] { new ServiceActionParameter("P1", customerType) }, null);
            operation.SetReadOnly();
            this.operationBoundToBaseType = new OperationWrapper(operation);

            this.entityToSerialize = EntityToSerialize.CreateFromExplicitValues(new object(), bestCustomerType, new TestSerializedEntityKey("http://odata.org/Service.svc/Customers/", bestCustomerType.FullName));

            var metadataUri = new Uri("http://odata.org/Service.svc/$metadata");
            this.testSubject = new OperationLinkBuilder("MyContainer", metadataUri);
        }
示例#3
0
        /// <summary>
        /// Get the ODataStreamReferenceValue instance containing the metadata for named stream property.
        /// </summary>
        /// <param name="entityToSerialize">Entity that is currently being serialized.</param>
        /// <param name="namedStreamProperty">Named stream property for which the link element needs to be written.</param>
        /// <returns>
        /// An instance of ODataStreamReferenceValue containing all the metadata about the named stream property.
        /// </returns>
        private ODataStreamReferenceValue GetNamedStreamPropertyValue(EntityToSerialize entityToSerialize, ResourceProperty namedStreamProperty)
        {
            Debug.Assert(entityToSerialize != null, "entityToSerialize != null");
            Debug.Assert(namedStreamProperty != null, "namedStreamProperty != null");
            Debug.Assert(namedStreamProperty.IsOfKind(ResourcePropertyKind.Stream), "namedStreamProperty.IsOfKind(ResourcePropertyKind.Stream)");

            string mediaETag;
            Uri    readStreamUri;
            string mediaContentType;

            this.Service.StreamProvider.GetStreamDescription(entityToSerialize.Entity, namedStreamProperty, this.Service.OperationContext, out mediaETag, out readStreamUri, out mediaContentType);
            Debug.Assert(WebUtil.IsETagValueValid(mediaETag, true), "WebUtil.IsETagValueValid(mediaETag, true)");

            ODataStreamReferenceValue streamReferenceValue = new ODataStreamReferenceValue();

            this.PayloadMetadataPropertyManager.SetContentType(streamReferenceValue, mediaContentType);

            this.PayloadMetadataPropertyManager.SetEditLink(
                streamReferenceValue,
                () =>
            {
                Uri relativeUri = entityToSerialize.SerializedKey.RelativeEditLink;
                return(RequestUriProcessor.AppendUnescapedSegment(relativeUri, namedStreamProperty.Name));
            });

            if (!string.IsNullOrEmpty(mediaETag))
            {
                this.PayloadMetadataPropertyManager.SetETag(streamReferenceValue, mediaETag);
            }

            // Note if readStreamUri is null, the self link for the named stream will be omitted.
            this.PayloadMetadataPropertyManager.SetReadLink(streamReferenceValue, () => readStreamUri);
            return(streamReferenceValue);
        }
        public OperationSerializerTests()
        {
            ResourceType intType = ResourceType.GetPrimitiveResourceType(typeof(int));

            var customerType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "FQ.NS", "Customer", false);
            customerType.CanReflectOnInstanceType = false;
            customerType.AddProperty(new ResourceProperty("Id", ResourcePropertyKind.Primitive | ResourcePropertyKind.Key, intType) { CanReflectOnInstanceTypeProperty = false });
            customerType.SetReadOnly();

            var operation = new ServiceAction("Action", intType, OperationParameterBindingKind.Sometimes, new[] { new ServiceActionParameter("P1", customerType), new ServiceActionParameter("P2", intType) }, null);
            operation.SetReadOnly();
            this.baseTypeOperation = new OperationWrapper(operation);

            var bestCustomerType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, customerType, "FQ.NS", "BestCustomer", false);
            bestCustomerType.SetReadOnly();

            operation = new ServiceAction("Action", intType, OperationParameterBindingKind.Sometimes, new[] { new ServiceActionParameter("P1", bestCustomerType) }, null);
            operation.SetReadOnly();
            this.derivedTypeOperation = new OperationWrapper(operation);

            operation = new ServiceAction("Unambiguous", intType, OperationParameterBindingKind.Sometimes, new[] { new ServiceActionParameter("P1", customerType) }, null);
            operation.SetReadOnly();
            this.unambiguousOperation = new OperationWrapper(operation);

            this.entityToSerialize = EntityToSerialize.CreateFromExplicitValues(new object(), bestCustomerType, new TestSerializedEntityKey("http://odata.org/Service.svc/Customers(0)/", bestCustomerType.FullName));

            this.testSubject = CreateOperationSerializer(AlwaysAdvertiseActions);
        }
示例#5
0
        /// <summary>Gets properties of the given entity type instance.</summary>
        /// <param name="entityToSerialize">Entity that is currently being serialized.</param>
        /// <param name="projectionNodesForCurrentResourceType">List of all the properties that are being projected for the resource type, or null if no projections are applied.
        ///   The list must be filtered to only nodes which apply to the current resource type.</param>
        /// <returns>The list of properties for the specified entity.</returns>
        private IEnumerable <ODataProperty> GetEntityProperties(EntityToSerialize entityToSerialize, IEnumerable <ProjectionNode> projectionNodesForCurrentResourceType)
        {
            Debug.Assert(entityToSerialize != null, "entityToSerialize != null");
            Debug.Assert(
                projectionNodesForCurrentResourceType == null ||
                projectionNodesForCurrentResourceType.All(projectionNode => projectionNode.TargetResourceType.IsAssignableFrom(entityToSerialize.ResourceType)),
                "The projection nodes must be filtered to the current resource type only.");

            this.RecurseEnter();
            try
            {
                Debug.Assert(this.CurrentContainer != null, "this.CurrentContainer != null");

                if (projectionNodesForCurrentResourceType == null)
                {
                    return(this.GetAllEntityProperties(entityToSerialize));
                }
                else
                {
                    // Fill only the properties which we know we will need values for.
                    return(this.GetProjectedEntityProperties(entityToSerialize, projectionNodesForCurrentResourceType));
                }
            }
            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();
            }
        }
示例#6
0
        /// <summary>Returns an IEnumerable of ODataProperty instance for all structural properties in the current resource type
        /// and populates the navigation property information along with association links.</summary>
        /// <param name="entityToSerialize">Entity that is currently being serialized.</param>
        /// <returns>Returns an IEnumerable of ODataProperty instance for all structural properties in the current resource type..</returns>
        private IEnumerable <ODataProperty> GetAllEntityProperties(EntityToSerialize entityToSerialize)
        {
            Debug.Assert(entityToSerialize != null, "entityToSerialize != null");

            List <ODataProperty> properties = new List <ODataProperty>(entityToSerialize.ResourceType.Properties.Count);

            foreach (ResourceProperty property in this.Provider.GetResourceSerializableProperties(this.CurrentContainer, entityToSerialize.ResourceType))
            {
                if (property.TypeKind != ResourceTypeKind.EntityType)
                {
                    properties.Add(this.GetODataPropertyForEntityProperty(entityToSerialize, property));
                }
            }

            if (entityToSerialize.ResourceType.IsOpenType)
            {
                foreach (KeyValuePair <string, object> property in this.Provider.GetOpenPropertyValues(entityToSerialize.Entity))
                {
                    string propertyName = property.Key;
                    if (string.IsNullOrEmpty(propertyName))
                    {
                        throw new DataServiceException(500, Microsoft.OData.Service.Strings.Syndication_InvalidOpenPropertyName(entityToSerialize.ResourceType.FullName));
                    }

                    properties.Add(this.GetODataPropertyForOpenProperty(propertyName, property.Value));
                }
            }

            return(properties);
        }
示例#7
0
        /// <summary>Returns an IEnumerable of ODataProperty instance for all projected properties in the <paramref name="projectionNodesForCurrentResourceType"/>
        /// and populates the navigation property information along with association links.</summary>
        /// <param name="entityToSerialize">Entity that is currently being serialized.</param>
        /// <param name="projectionNodesForCurrentResourceType">List of all the properties that are being projected for the resource type, or null if no projections are applied.
        ///   The list must be filtered to only nodes which apply to the current resource type.</param>
        /// <returns>Returns an IEnumerable of ODataProperty instance for all structural properties in the current resource type.</returns>
        private IEnumerable <ODataProperty> GetProjectedEntityProperties(EntityToSerialize entityToSerialize, IEnumerable <ProjectionNode> projectionNodesForCurrentResourceType)
        {
            Debug.Assert(entityToSerialize != null, "entityToSerialize != null");
            Debug.Assert(
                projectionNodesForCurrentResourceType == null ||
                projectionNodesForCurrentResourceType.All(projectionNode => projectionNode.TargetResourceType.IsAssignableFrom(entityToSerialize.ResourceType)),
                "The projection nodes must be filtered to the current resource type only.");

            List <ODataProperty> properties = new List <ODataProperty>(entityToSerialize.ResourceType.Properties.Count);

            foreach (ProjectionNode projectionNode in projectionNodesForCurrentResourceType)
            {
                string propertyName     = projectionNode.PropertyName;
                var    resourceProperty = projectionNode.Property;
                if (resourceProperty != null)
                {
                    if (resourceProperty.TypeKind != ResourceTypeKind.EntityType)
                    {
                        properties.Add(this.GetODataPropertyForEntityProperty(entityToSerialize, resourceProperty));
                    }
                }
                else
                {
                    // Now get the property value
                    object propertyValue = WebUtil.GetPropertyValue(this.Provider, entityToSerialize.Entity, entityToSerialize.ResourceType, null, propertyName);
                    properties.Add(this.GetODataPropertyForOpenProperty(propertyName, propertyValue));
                }
            }

            return(properties);
        }
示例#8
0
        /// <summary>
        /// Asks the provider if the action should be advertised in payloads.
        /// </summary>
        /// <param name="entityToSerialize">The entity to serialize.</param>
        /// <param name="resourceInstanceInFeed">Whether or not the entity is being serialized in a feed.</param>
        /// <param name="serviceOperationWrapper">The service operation wrapper.</param>
        /// <param name="lazyActionTargetUri">Target uri of the action, which will only be generated if needed.</param>
        /// <param name="entityHasMultipleActionsWithSameName">Whether or not there are multiple operations in the current scope with the same name as the current operation.</param>
        /// <param name="odataAction">The ODL object-model representation of the action.</param>
        /// <returns>Whether or not the action should be advertised.</returns>
        private bool AskProviderIfActionShouldBeAdvertised(EntityToSerialize entityToSerialize, bool resourceInstanceInFeed, OperationWrapper serviceOperationWrapper, SimpleLazy <Uri> lazyActionTargetUri, bool entityHasMultipleActionsWithSameName, ref ODataAction odataAction)
        {
            if (this.advertiseServiceAction(serviceOperationWrapper, entityToSerialize.Entity, resourceInstanceInFeed, ref odataAction))
            {
                if (odataAction == null)
                {
                    throw new DataServiceException(500, Microsoft.OData.Service.Strings.DataServiceActionProviderWrapper_AdvertiseServiceActionCannotReturnNullActionToSerialize);
                }

                // Always set target and title if there are overloaded actions.
                if (!entityHasMultipleActionsWithSameName)
                {
                    this.metadataPropertyManager.CheckForUnmodifiedTitle(odataAction, serviceOperationWrapper.Name);
                    this.metadataPropertyManager.CheckForUnmodifiedTarget(odataAction, () => lazyActionTargetUri.Value);
                }

                // make the target link relative
                this.MakeOperationTargetRelativeFromMetadataUriIfJsonLight(odataAction);

                return(true);
            }

            odataAction = null;
            return(false);
        }
示例#9
0
        /// <summary>
        /// Return the canonical uri (the edit link) of the element.
        /// </summary>
        /// <param name="element">Element whose canonical uri need to be returned.</param>
        /// <returns>Return the canonical uri of the element.</returns>
        private Uri GetEntityEditLink(object element)
        {
            Debug.Assert(element != null, "element != null");
            ResourceType      resourceType = WebUtil.GetNonPrimitiveResourceType(this.Provider, element);
            EntityToSerialize entity       = EntityToSerialize.Create(element, resourceType, this.CurrentContainer, this.Provider, this.AbsoluteServiceUri);

            return(entity.SerializedKey.AbsoluteEditLink);
        }
示例#10
0
        /// <summary>
        /// Get the stream reference value for media resource (the default stream of an entity).
        /// </summary>
        /// <param name="entityToSerialize">Entity that is currently being serialized.</param>
        /// <param name="title">The title for the element being written.</param>
        /// <returns>
        /// An instance of ODataStreamReferenceValue containing the metadata about the media resource.
        /// </returns>
        private ODataStreamReferenceValue GetMediaResource(EntityToSerialize entityToSerialize, string title)
        {
            Debug.Assert(entityToSerialize.Entity != null, "element != null");
            Debug.Assert(entityToSerialize.ResourceType != null && entityToSerialize.ResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "type != null && type.ResourceTypeKind == ResourceTypeKind.EntityType");
            Debug.Assert(!string.IsNullOrEmpty(title), "!string.IsNullOrEmpty(title)");

            ODataStreamReferenceValue mediaResource = null;

            // Handle MLE
            if (entityToSerialize.ResourceType.IsMediaLinkEntry)
            {
                string mediaETag;
                Uri    readStreamUri;
                string mediaContentType;

                Debug.Assert(entityToSerialize.ResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "type.ResourceTypeKind == ResourceTypeKind.EntityType");
                this.Service.StreamProvider.GetStreamDescription(entityToSerialize.Entity, null /*null for MLE*/, this.Service.OperationContext, out mediaETag, out readStreamUri, out mediaContentType);
                Debug.Assert(WebUtil.IsETagValueValid(mediaETag, true), "WebUtil.IsETagValueValid(mediaETag, true)");
                Debug.Assert(!string.IsNullOrEmpty(mediaContentType), "!string.IsNullOrEmpty(mediaContentType)");

                mediaResource = new ODataStreamReferenceValue();

                // build the stream's edit link lazily to avoid creating the entity's edit link if it is not needed.
                SimpleLazy <Uri> lazyStreamEditLink = new SimpleLazy <Uri>(() => RequestUriProcessor.AppendEscapedSegment(entityToSerialize.SerializedKey.RelativeEditLink, XmlConstants.UriValueSegment));

                this.PayloadMetadataPropertyManager.SetEditLink(mediaResource, () => lazyStreamEditLink.Value);

                this.PayloadMetadataPropertyManager.SetContentType(mediaResource, mediaContentType);

                // If the stream provider did not provider a read link, then we should use the edit link as the read link.
                this.PayloadMetadataPropertyManager.SetReadLink(mediaResource, () => readStreamUri ?? lazyStreamEditLink.Value);
#pragma warning disable 618
                if (this.contentFormat == ODataFormat.Atom)
#pragma warning restore 618
                {
                    AtomStreamReferenceMetadata mediaResourceAtom = new AtomStreamReferenceMetadata()
                    {
                        EditLink = new AtomLinkMetadata {
                            Title = title
                        }
                    };
                    mediaResource.SetAnnotation(mediaResourceAtom);
                }

                if (!string.IsNullOrEmpty(mediaETag))
                {
                    this.PayloadMetadataPropertyManager.SetETag(mediaResource, mediaETag);
                }
            }

            return(mediaResource);
        }
示例#11
0
        /// <summary>
        /// Tries to serialize the operation.
        /// </summary>
        /// <param name="entityToSerialize">The entity to serialize.</param>
        /// <param name="resourceInstanceInFeed">Whether or not the entity is being serialized in a feed.</param>
        /// <param name="entityHasMultipleActionsWithSameName">Whether or not there are multiple operations in the current scope with the same name as the current operation.</param>
        /// <param name="serviceOperationWrapper">The service operation wrapper.</param>
        /// <param name="odataAction">The ODL object-model representation of the action.</param>
        /// <returns>Whether or not to serialize the operation.</returns>
        private bool TrySerializeOperation(EntityToSerialize entityToSerialize, bool resourceInstanceInFeed, bool entityHasMultipleActionsWithSameName, OperationWrapper serviceOperationWrapper, out ODataAction odataAction)
        {
            Debug.Assert(serviceOperationWrapper != null, "serviceOperationWrapper != null");

            // We only advertise actions. This is a debug assert because GetServiceOperationsByResourceType only returns actions.
            Debug.Assert(serviceOperationWrapper.Kind == OperationKind.Action, "Only actions can be advertised");

            Uri metadata = this.operationLinkBuilder.BuildMetadataLink(serviceOperationWrapper, entityHasMultipleActionsWithSameName);

            // If the action has OperationParameterBindingKind set to "Always" then we advertise the action without calling "AdvertiseServiceAction".
            bool isAlwaysAvailable = serviceOperationWrapper.OperationParameterBindingKind == OperationParameterBindingKind.Always;

            odataAction = new ODataAction {
                Metadata = metadata
            };

            // There is some subtlety to the interaction between action advertisement and whether or not to include title/target on the wire.
            //
            // 1) If an action is always available:
            //    The provider author does not get a chance to customize the title/target values...
            //    so the values will be based on conventions...
            //    so by default do not write them on the wire
            // 2) If it is only sometimes available:
            //    The values need to be computed to provide them on the instance given to the provider...
            //    but they might not be changed by the provider author...
            //    so compare them to the computed values, and do not write them if they match.

            // TODO: Action provider should be able to customize title/target even if the action is 'always' advertised
            // If this gets fixed, then all the behavior should collapse to emulate case #2 above

            // Create a lazy Uri for the target, because we may need it more than once (see case #2 above).
            SimpleLazy <Uri> lazyActionTargetUri = new SimpleLazy <Uri>(() => this.operationLinkBuilder.BuildTargetLink(entityToSerialize, serviceOperationWrapper, entityHasMultipleActionsWithSameName));

            this.metadataPropertyManager.SetTitle(odataAction, isAlwaysAvailable, serviceOperationWrapper.Name);
            this.metadataPropertyManager.SetTarget(odataAction, isAlwaysAvailable, () => lazyActionTargetUri.Value);

            // If the operation is always available,
            // 1. Return true for MetadataQueryOption.All.
            // 2. Return false for MetadataQueryOption.None.
            // 3. Return false for MetadataQueryOption.Default.
            if (isAlwaysAvailable)
            {
                return(this.payloadMetadataParameterInterpreter.ShouldIncludeAlwaysAvailableOperation());
            }

            return(this.AskProviderIfActionShouldBeAdvertised(entityToSerialize, resourceInstanceInFeed, serviceOperationWrapper, lazyActionTargetUri, entityHasMultipleActionsWithSameName, ref odataAction));
        }
示例#12
0
        private void WriteAllNestedComplexProperties(EntityToSerialize entityToSerialize, IEnumerable <ProjectionNode> projectionNodesForCurrentResourceType)
        {
            Debug.Assert(entityToSerialize != null, "entityToSerialize != null");

            var properties = projectionNodesForCurrentResourceType == null
                ? this.Provider.GetResourceSerializableProperties(this.CurrentContainer, entityToSerialize.ResourceType)
                : projectionNodesForCurrentResourceType.Select(p => p.Property);

            foreach (ResourceProperty property in properties)
            {
                if (property != null && (property.TypeKind == ResourceTypeKind.ComplexType ||
                                         (property.TypeKind == ResourceTypeKind.Collection && property.ResourceType.ElementType().ResourceTypeKind == ResourceTypeKind.ComplexType)))
                {
                    ODataWriterHelper.WriteNestedResourceInfo(this.dataServicesODataWriter.InnerWriter, this.GetODataNestedResourceForComplexProperty(entityToSerialize, property));
                }
            }
        }
示例#13
0
        /// <summary>
        /// Gets the target link value for an <see cref="ODataOperation"/>
        /// </summary>
        /// <param name="entityToSerialize">The current entity being serialized.</param>
        /// <param name="operation">The operation to generate the link for.</param>
        /// <param name="entityHasMultipleActionsWithSameName">Whether or not there are multiple operations in the current scope with the same name as the current operation.</param>
        /// <returns>Uri representing link to use for invoking this operation.</returns>
        internal Uri BuildTargetLink(EntityToSerialize entityToSerialize, OperationWrapper operation, bool entityHasMultipleActionsWithSameName)
        {
            Debug.Assert(entityToSerialize != null, "entityToSerialize != null");
            Debug.Assert(operation != null, "operation != null");
            Debug.Assert(operation.BindingParameter != null, "operation.BindingParameter != null");
            Debug.Assert(operation.BindingParameter.ParameterType != null, "operation.BindingParameter.ParameterType != null");

            string targetSegment = operation.GetActionTargetSegmentByResourceType(entityToSerialize.ResourceType, this.namespaceName);

            // If there are multiple operations with the same name, then using the edit link of the entry would cause the target to potentially resolve to the wrong
            // operation. Instead, use the actual binding type of the specific operation.
            if (entityHasMultipleActionsWithSameName)
            {
                Uri editLinkWithBindingType = RequestUriProcessor.AppendUnescapedSegment(entityToSerialize.SerializedKey.AbsoluteEditLinkWithoutSuffix, operation.BindingParameter.ParameterType.FullName);
                return(RequestUriProcessor.AppendUnescapedSegment(editLinkWithBindingType, targetSegment));
            }

            return(RequestUriProcessor.AppendUnescapedSegment(entityToSerialize.SerializedKey.AbsoluteEditLink, targetSegment));
        }
示例#14
0
        /// <summary>
        /// Gets the target link value for an <see cref="ODataOperation"/>
        /// </summary>
        /// <param name="entityToSerialize">The current entity being serialized.</param>
        /// <param name="operation">The operation to generate the link for.</param>
        /// <param name="entityHasMultipleActionsWithSameName">Whether or not there are multiple operations in the current scope with the same name as the current operation.</param>
        /// <returns>Uri representing link to use for invoking this operation.</returns>
        internal Uri BuildTargetLink(EntityToSerialize entityToSerialize, OperationWrapper operation, bool entityHasMultipleActionsWithSameName)
        {
            Debug.Assert(entityToSerialize != null, "entityToSerialize != null");
            Debug.Assert(operation != null, "operation != null");
            Debug.Assert(operation.BindingParameter != null, "operation.BindingParameter != null");
            Debug.Assert(operation.BindingParameter.ParameterType != null, "operation.BindingParameter.ParameterType != null");

            string targetSegment = operation.GetActionTargetSegmentByResourceType(entityToSerialize.ResourceType, this.namespaceName);

            // If there are multiple operations with the same name, then using the edit link of the entry would cause the target to potentially resolve to the wrong
            // operation. Instead, use the actual binding type of the specific operation.
            if (entityHasMultipleActionsWithSameName)
            {
                Uri editLinkWithBindingType = RequestUriProcessor.AppendUnescapedSegment(entityToSerialize.SerializedKey.AbsoluteEditLinkWithoutSuffix, operation.BindingParameter.ParameterType.FullName);
                return RequestUriProcessor.AppendUnescapedSegment(editLinkWithBindingType, targetSegment);
            }

            return RequestUriProcessor.AppendUnescapedSegment(entityToSerialize.SerializedKey.AbsoluteEditLink, targetSegment);
        }
示例#15
0
        /// <summary>
        /// Serializes the given operations and returns the resulting instances of <see cref="ODataAction"/>.
        /// </summary>
        /// <param name="entityToSerialize">The entity to serialize.</param>
        /// <param name="resourceInstanceInFeed">Whether or not the entity is being serialized in a feed.</param>
        /// <param name="operationWrappers">The wrapped operations to serialize.</param>
        /// <returns>The serialized actions.</returns>
        internal IEnumerable <ODataAction> SerializeOperations(EntityToSerialize entityToSerialize, bool resourceInstanceInFeed, ICollection <OperationWrapper> operationWrappers)
        {
            Debug.Assert(operationWrappers != null, "serviceOperationWrapperList != null");

            var odataActions = new List <ODataAction>(operationWrappers.Count);

            // Create a hashset containing the names of all actions in the current scope that are duplicates.
            HashSet <string> collidingActionNames = GetCollidingActionNames(operationWrappers);

            foreach (OperationWrapper wrapper in operationWrappers)
            {
                bool actionNameHasCollision = collidingActionNames.Contains(wrapper.Name);

                ODataAction odataAction;
                if (this.TrySerializeOperation(entityToSerialize, resourceInstanceInFeed, actionNameHasCollision, wrapper, out odataAction))
                {
                    odataActions.Add(odataAction);
                }
            }

            return(odataActions);
        }
示例#16
0
        /// <summary>
        /// Tries to build the set of <see cref="ODataAction"/> to be advertised for the given resource.
        /// </summary>
        /// <param name="entityToSerialize">Entity that is currently being serialized.</param>
        /// <param name="resourceInstanceInFeed">true if the resource instance being serialized is inside a feed; false otherwise.</param>
        /// <param name="actions">The actions to advertise.</param>
        /// <returns>
        /// Whether any actions should be advertised.
        /// </returns>
        private bool TryGetAdvertisedActions(EntityToSerialize entityToSerialize, bool resourceInstanceInFeed, out IEnumerable <ODataAction> actions)
        {
            Debug.Assert(entityToSerialize != null, "entityToSerialize != null");

            if (!this.Service.ActionProvider.IsImplemented)
            {
                actions = null;
                return(false);
            }

            // If GetOperationProjections returns null it means no projections are to be applied and all operations
            // for the current segment should be serialized. If it returns non-null only the operations described
            // by the returned projection segments should be serialized.
            ExpandedProjectionNode expandedProjectionNode = this.GetCurrentExpandedProjectionNode();

            List <OperationWrapper> serviceOperationWrapperList;

            if (expandedProjectionNode == null || expandedProjectionNode.ProjectAllOperations)
            {
                // Note that if the service does not implement IDataServiceActionProvider and the MaxProtocolVersion < V3,
                // GetServiceActionsByBindingParameterType() would simply return an empty ServiceOperationWrapper collection.
                serviceOperationWrapperList = this.Service.ActionProvider.GetServiceActionsByBindingParameterType(entityToSerialize.ResourceType);
            }
            else
            {
                serviceOperationWrapperList = expandedProjectionNode.SelectedOperations.GetSelectedOperations(entityToSerialize.ResourceType);
            }

            Debug.Assert(serviceOperationWrapperList != null, "serviceOperationWrappers != null");
            if (serviceOperationWrapperList.Count <= 0)
            {
                actions = null;
                return(false);
            }

            actions = this.OperationSerializer.SerializeOperations(entityToSerialize, resourceInstanceInFeed, serviceOperationWrapperList);
            return(true);
        }
示例#17
0
        /// <summary>
        /// Creates the navigation link for the given navigation property.
        /// </summary>
        /// <param name="entityToSerialize">Entity that is currently being serialized.</param>
        /// <param name="navigationProperty">The metadata for the navigation property.</param>
        /// <returns>The navigation link for the given property.</returns>
        private ODataNavigationLink GetNavigationLink(EntityToSerialize entityToSerialize, ResourceProperty navigationProperty)
        {
            Debug.Assert(entityToSerialize != null, "entityToSerialize != null");
            Debug.Assert(navigationProperty != null, "navigationProperty != null");

            string navLinkName          = navigationProperty.Name;
            ODataNavigationLink navLink = new ODataNavigationLink
            {
                Name         = navLinkName,
                IsCollection = navigationProperty.Kind == ResourcePropertyKind.ResourceSetReference
            };

            // Always pass the relative uri to the ODatalib. For Json, they will convert the relative uri
            // into absolute uri using the BaseUri property on the ODataWriterSettings. For atom, ODataLib
            // will write the relative uri.
            this.PayloadMetadataPropertyManager.SetUrl(navLink, () => RequestUriProcessor.AppendUnescapedSegment(entityToSerialize.SerializedKey.RelativeEditLink, navLinkName));

            if (this.Service.Configuration.DataServiceBehavior.IncludeAssociationLinksInResponse)
            {
                this.PayloadMetadataPropertyManager.SetAssociationLinkUrl(navLink, () => GetAssociationLinkUrl(entityToSerialize, navigationProperty));
            }

            return(navLink);
        }
示例#18
0
        private void WriteNavigationProperties(IExpandedResult expanded, EntityToSerialize entityToSerialize, bool resourceInstanceInFeed, IEnumerable <ProjectionNode> projectionNodesForCurrentResourceType)
        {
            Debug.Assert(entityToSerialize != null, "entityToSerialize != null");
            Debug.Assert(
                projectionNodesForCurrentResourceType == null ||
                projectionNodesForCurrentResourceType.All(projectionNode => projectionNode.TargetResourceType.IsAssignableFrom(entityToSerialize.ResourceType)),
                "The projection nodes must be filtered to the current resource type only.");

            IEnumerable <ResourceProperty> navProperties =
                projectionNodesForCurrentResourceType == null
                ? this.Provider.GetResourceSerializableProperties(this.CurrentContainer, entityToSerialize.ResourceType).Where(p => p.TypeKind == ResourceTypeKind.EntityType)
                : projectionNodesForCurrentResourceType.Where(p => p.Property != null && p.Property.TypeKind == ResourceTypeKind.EntityType).Select(p1 => p1.Property);

            foreach (ResourceProperty property in navProperties)
            {
                ResourcePropertyInfo navProperty = this.GetNavigationPropertyInfo(expanded, entityToSerialize.Entity, entityToSerialize.ResourceType, property);
                ODataNavigationLink  navLink     = this.GetNavigationLink(entityToSerialize, navProperty.Property);

                // WCF Data Services show performance degradation with JsonLight when entities have > 25 Navgations on writing entries
                // DEVNOTE: for performance reasons, if the link has no content due to the metadata level, and is not expanded
                // then don't tell ODataLib about it at all.
                if (navLink.Url == null && navLink.AssociationLinkUrl == null && !navProperty.Expand)
                {
                    continue;
                }

                var linkArgs = new DataServiceODataWriterNavigationLinkArgs(navLink, this.Service.OperationContext);
                this.dataServicesODataWriter.WriteStart(linkArgs);

                if (navProperty.Expand)
                {
                    object          navPropertyValue          = navProperty.Value;
                    IExpandedResult navPropertyExpandedResult = navPropertyValue as IExpandedResult;
                    object          expandedPropertyValue     =
                        navPropertyExpandedResult != null?
                        GetExpandedElement(navPropertyExpandedResult) :
                            navPropertyValue;

                    bool needPop = this.PushSegmentForProperty(navProperty.Property, entityToSerialize.ResourceType, navProperty.ExpandedNode);

                    // if this.CurrentContainer is null, the target set of the navigation property is hidden.
                    if (this.CurrentContainer != null)
                    {
                        if (navProperty.Property.Kind == 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");

                            QueryResultInfo queryResults = new QueryResultInfo(enumerable);
                            try
                            {
                                queryResults.MoveNext();
                                Func <Uri> getNavPropertyRelativeUri = () => RequestUriProcessor.AppendUnescapedSegment(entityToSerialize.SerializedKey.RelativeEditLink, navLink.Name);
                                Func <Uri> getNavPropertyAbsoluteUri = () => RequestUriProcessor.AppendUnescapedSegment(entityToSerialize.SerializedKey.AbsoluteEditLink, navLink.Name);
                                this.WriteFeedElements(navPropertyExpandedResult, queryResults, navProperty.Property.ResourceType, navLink.Name, getNavPropertyRelativeUri, getNavPropertyAbsoluteUri, false);
                            }
                            finally
                            {
                                // After the navigation property is completely serialized, dispose the property value.
                                WebUtil.Dispose(queryResults);
                            }
                        }
                        else if (WebUtil.IsNullValue(expandedPropertyValue))
                        {
                            // Write a null reference navigation property
                            var entryArgs = new DataServiceODataWriterEntryArgs(null, null, this.Service.OperationContext);
                            this.dataServicesODataWriter.WriteStart(entryArgs);
                            this.dataServicesODataWriter.WriteEnd(entryArgs);
                        }
                        else
                        {
                            this.WriteEntry(navPropertyExpandedResult, expandedPropertyValue, resourceInstanceInFeed, navProperty.Property.ResourceType);
                        }
                    }

                    this.PopSegmentName(needPop);
                }

                this.dataServicesODataWriter.WriteEnd(linkArgs); // end of navigation property
            }
        }
示例#19
0
        /// <summary>Gets properties of the given entity type instance.</summary>
        /// <param name="entityToSerialize">Entity that is currently being serialized.</param>
        /// <param name="projectionNodesForCurrentResourceType">List of all the properties that are being projected for the resource type, or null if no projections are applied.
        ///   The list must be filtered to only nodes which apply to the current resource type.</param>
        /// <returns>The list of properties for the specified entity.</returns>
        private IEnumerable<ODataProperty> GetEntityProperties(EntityToSerialize entityToSerialize, IEnumerable<ProjectionNode> projectionNodesForCurrentResourceType)
        {
            Debug.Assert(entityToSerialize != null, "entityToSerialize != null");
            Debug.Assert(
                projectionNodesForCurrentResourceType == null ||
                projectionNodesForCurrentResourceType.All(projectionNode => projectionNode.TargetResourceType.IsAssignableFrom(entityToSerialize.ResourceType)),
                "The projection nodes must be filtered to the current resource type only.");

            this.RecurseEnter();
            try
            {
                Debug.Assert(this.CurrentContainer != null, "this.CurrentContainer != null");

                if (projectionNodesForCurrentResourceType == null)
                {
                    return this.GetAllEntityProperties(entityToSerialize);
                }
                else
                {
                    // Fill only the properties which we know we will need values for.
                    return this.GetProjectedEntityProperties(entityToSerialize, projectionNodesForCurrentResourceType);
                }
            }
            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();
            }
        }
示例#20
0
        /// <summary>Returns an IEnumerable of ODataProperty instance for all structural properties in the current resource type
        /// and populates the navigation property information along with association links.</summary>
        /// <param name="entityToSerialize">Entity that is currently being serialized.</param>
        /// <returns>Returns an IEnumerable of ODataProperty instance for all structural properties in the current resource type..</returns>
        private IEnumerable<ODataProperty> GetAllEntityProperties(EntityToSerialize entityToSerialize)
        {
            Debug.Assert(entityToSerialize != null, "entityToSerialize != null");
            
            List<ODataProperty> properties = new List<ODataProperty>(entityToSerialize.ResourceType.Properties.Count);
            foreach (ResourceProperty property in this.Provider.GetResourceSerializableProperties(this.CurrentContainer, entityToSerialize.ResourceType))
            {
                if (property.TypeKind != ResourceTypeKind.EntityType)
                {
                    properties.Add(this.GetODataPropertyForEntityProperty(entityToSerialize, property));
                }
            }

            if (entityToSerialize.ResourceType.IsOpenType)
            {
                foreach (KeyValuePair<string, object> property in this.Provider.GetOpenPropertyValues(entityToSerialize.Entity))
                {
                    string propertyName = property.Key;
                    if (string.IsNullOrEmpty(propertyName))
                    {
                        throw new DataServiceException(500, Microsoft.OData.Service.Strings.Syndication_InvalidOpenPropertyName(entityToSerialize.ResourceType.FullName));
                    }

                    properties.Add(this.GetODataPropertyForOpenProperty(propertyName, property.Value));
                }
            }

            return properties;
        }
示例#21
0
        /// <summary>Returns an IEnumerable of ODataProperty instance for all projected properties in the <paramref name="projectionNodesForCurrentResourceType"/>
        /// and populates the navigation property information along with association links.</summary>
        /// <param name="entityToSerialize">Entity that is currently being serialized.</param>
        /// <param name="projectionNodesForCurrentResourceType">List of all the properties that are being projected for the resource type, or null if no projections are applied.
        ///   The list must be filtered to only nodes which apply to the current resource type.</param>
        /// <returns>Returns an IEnumerable of ODataProperty instance for all structural properties in the current resource type.</returns>
        private IEnumerable<ODataProperty> GetProjectedEntityProperties(EntityToSerialize entityToSerialize, IEnumerable<ProjectionNode> projectionNodesForCurrentResourceType)
        {
            Debug.Assert(entityToSerialize != null, "entityToSerialize != null");
            Debug.Assert(
                projectionNodesForCurrentResourceType == null ||
                projectionNodesForCurrentResourceType.All(projectionNode => projectionNode.TargetResourceType.IsAssignableFrom(entityToSerialize.ResourceType)),
                "The projection nodes must be filtered to the current resource type only.");

            List<ODataProperty> properties = new List<ODataProperty>(entityToSerialize.ResourceType.Properties.Count);
            foreach (ProjectionNode projectionNode in projectionNodesForCurrentResourceType)
            {
                string propertyName = projectionNode.PropertyName;
                var resourceProperty = projectionNode.Property;
                if (resourceProperty != null)
                {
                    if (resourceProperty.TypeKind != ResourceTypeKind.EntityType)
                    {
                        properties.Add(this.GetODataPropertyForEntityProperty(entityToSerialize, resourceProperty));
                    }
                }
                else
                {
                    // Now get the property value
                    object propertyValue = WebUtil.GetPropertyValue(this.Provider, entityToSerialize.Entity, entityToSerialize.ResourceType, null, propertyName);
                    properties.Add(this.GetODataPropertyForOpenProperty(propertyName, propertyValue));
                }
            }

            return properties;
        }
示例#22
0
        private void WriteNavigationProperties(IExpandedResult expanded, EntityToSerialize entityToSerialize, bool resourceInstanceInFeed, IEnumerable<ProjectionNode> projectionNodesForCurrentResourceType)
        {
            Debug.Assert(entityToSerialize != null, "entityToSerialize != null");
            Debug.Assert(
                projectionNodesForCurrentResourceType == null ||
                projectionNodesForCurrentResourceType.All(projectionNode => projectionNode.TargetResourceType.IsAssignableFrom(entityToSerialize.ResourceType)),
                "The projection nodes must be filtered to the current resource type only.");

            IEnumerable<ResourceProperty> navProperties =
                projectionNodesForCurrentResourceType == null
                ? this.Provider.GetResourceSerializableProperties(this.CurrentContainer, entityToSerialize.ResourceType).Where(p => p.TypeKind == ResourceTypeKind.EntityType)
                : projectionNodesForCurrentResourceType.Where(p => p.Property != null && p.Property.TypeKind == ResourceTypeKind.EntityType).Select(p1 => p1.Property);

            foreach (ResourceProperty property in navProperties)
            {
                ResourcePropertyInfo navProperty = this.GetNavigationPropertyInfo(expanded, entityToSerialize.Entity, entityToSerialize.ResourceType, property);
                ODataNavigationLink navLink = this.GetNavigationLink(entityToSerialize, navProperty.Property);

                // WCF Data Services show performance degradation with JsonLight when entities have > 25 Navgations on writing entries
                // DEVNOTE: for performance reasons, if the link has no content due to the metadata level, and is not expanded
                // then don't tell ODataLib about it at all.
                if (navLink.Url == null && navLink.AssociationLinkUrl == null && !navProperty.Expand)
                {
                    continue;
                }

                var linkArgs = new DataServiceODataWriterNavigationLinkArgs(navLink, this.Service.OperationContext);
                this.dataServicesODataWriter.WriteStart(linkArgs);

                if (navProperty.Expand)
                {
                    object navPropertyValue = navProperty.Value;
                    IExpandedResult navPropertyExpandedResult = navPropertyValue as IExpandedResult;
                    object expandedPropertyValue =
                                navPropertyExpandedResult != null ?
                                GetExpandedElement(navPropertyExpandedResult) :
                                navPropertyValue;

                    bool needPop = this.PushSegmentForProperty(navProperty.Property, entityToSerialize.ResourceType, navProperty.ExpandedNode);

                    // if this.CurrentContainer is null, the target set of the navigation property is hidden.
                    if (this.CurrentContainer != null)
                    {
                        if (navProperty.Property.Kind == 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");

                            QueryResultInfo queryResults = new QueryResultInfo(enumerable);
                            try
                            {
                                queryResults.MoveNext();
                                Func<Uri> getNavPropertyRelativeUri = () => RequestUriProcessor.AppendUnescapedSegment(entityToSerialize.SerializedKey.RelativeEditLink, navLink.Name);
                                Func<Uri> getNavPropertyAbsoluteUri = () => RequestUriProcessor.AppendUnescapedSegment(entityToSerialize.SerializedKey.AbsoluteEditLink, navLink.Name);
                                this.WriteFeedElements(navPropertyExpandedResult, queryResults, navProperty.Property.ResourceType, navLink.Name, getNavPropertyRelativeUri, getNavPropertyAbsoluteUri, false);
                            }
                            finally
                            {
                                // After the navigation property is completely serialized, dispose the property value.
                                WebUtil.Dispose(queryResults);
                            }
                        }
                        else if (WebUtil.IsNullValue(expandedPropertyValue))
                        {
                            // Write a null reference navigation property
                            var entryArgs = new DataServiceODataWriterEntryArgs(null, null, this.Service.OperationContext);
                            this.dataServicesODataWriter.WriteStart(entryArgs);
                            this.dataServicesODataWriter.WriteEnd(entryArgs);
                        }
                        else
                        {
                            this.WriteEntry(navPropertyExpandedResult, expandedPropertyValue, resourceInstanceInFeed, navProperty.Property.ResourceType);
                        }
                    }

                    this.PopSegmentName(needPop);
                }

                this.dataServicesODataWriter.WriteEnd(linkArgs); // end of navigation property
            }
        }
示例#23
0
        /// <summary>
        /// Creates the navigation link for the given navigation property.
        /// </summary>
        /// <param name="entityToSerialize">Entity that is currently being serialized.</param>
        /// <param name="navigationProperty">The metadata for the navigation property.</param>
        /// <returns>The navigation link for the given property.</returns>
        private ODataNavigationLink GetNavigationLink(EntityToSerialize entityToSerialize, ResourceProperty navigationProperty)
        {
            Debug.Assert(entityToSerialize != null, "entityToSerialize != null");
            Debug.Assert(navigationProperty != null, "navigationProperty != null");

            string navLinkName = navigationProperty.Name;
            ODataNavigationLink navLink = new ODataNavigationLink
            {
                Name = navLinkName,
                IsCollection = navigationProperty.Kind == ResourcePropertyKind.ResourceSetReference
            };

            // Always pass the relative uri to the ODatalib. For Json, they will convert the relative uri
            // into absolute uri using the BaseUri property on the ODataWriterSettings. For atom, ODataLib
            // will write the relative uri.
            this.PayloadMetadataPropertyManager.SetUrl(navLink, () => RequestUriProcessor.AppendUnescapedSegment(entityToSerialize.SerializedKey.RelativeEditLink, navLinkName));

            if (this.Service.Configuration.DataServiceBehavior.IncludeAssociationLinksInResponse)
            {
                this.PayloadMetadataPropertyManager.SetAssociationLinkUrl(navLink, () => GetAssociationLinkUrl(entityToSerialize, navigationProperty));
            }

            return navLink;
        }
示例#24
0
        /// <summary>Write the entry element.</summary>
        /// <param name="expanded">Expanded result provider for the specified <paramref name="element"/>.</param>
        /// <param name="element">Element representing the entry element.</param>
        /// <param name="resourceInstanceInFeed">true if the resource instance being serialized is inside a feed; false otherwise.</param>
        /// <param name="expectedType">Expected type of the entry element.</param>
        private void WriteEntry(IExpandedResult expanded, object element, bool resourceInstanceInFeed, ResourceType expectedType)
        {
            Debug.Assert(element != null, "element != null");
            Debug.Assert(expectedType != null && expectedType.ResourceTypeKind == ResourceTypeKind.EntityType, "expectedType != null && expectedType.ResourceTypeKind == ResourceTypeKind.EntityType");
            this.IncrementSegmentResultCount();

            ODataEntry entry = new ODataEntry();

            if (!resourceInstanceInFeed)
            {
                entry.SetSerializationInfo(new ODataFeedAndEntrySerializationInfo {
                    NavigationSourceName = this.CurrentContainer.Name, NavigationSourceEntityTypeName = this.CurrentContainer.ResourceType.FullName, ExpectedTypeName = expectedType.FullName
                });
            }

            string title = expectedType.Name;

#pragma warning disable 618
            if (this.contentFormat == ODataFormat.Atom)
#pragma warning restore 618
            {
                AtomEntryMetadata entryAtom = new AtomEntryMetadata();
                entryAtom.EditLink = new AtomLinkMetadata {
                    Title = title
                };
                entry.SetAnnotation(entryAtom);
            }

            ResourceType actualResourceType = WebUtil.GetNonPrimitiveResourceType(this.Provider, element);
            if (actualResourceType.ResourceTypeKind != ResourceTypeKind.EntityType)
            {
                // making sure that the actual resource type is an entity type
                throw new DataServiceException(500, Microsoft.OData.Service.Strings.BadProvider_InconsistentEntityOrComplexTypeUsage(actualResourceType.FullName));
            }

            EntityToSerialize entityToSerialize = this.WrapEntity(element, actualResourceType);

            // populate the media resource, if the entity is a MLE.
            entry.MediaResource = this.GetMediaResource(entityToSerialize, title);

            // Write the type name
            this.PayloadMetadataPropertyManager.SetTypeName(entry, this.CurrentContainer.ResourceType.FullName, actualResourceType.FullName);

            // Write Id element
            this.PayloadMetadataPropertyManager.SetId(entry, () => entityToSerialize.SerializedKey.Identity);

            // Write "edit" link
            this.PayloadMetadataPropertyManager.SetEditLink(entry, () => entityToSerialize.SerializedKey.RelativeEditLink);

            // Write the etag property, if the type has etag properties
            this.PayloadMetadataPropertyManager.SetETag(entry, () => this.GetETagValue(element, actualResourceType));

            IEnumerable <ProjectionNode> projectionNodes = this.GetProjections();
            if (projectionNodes != null)
            {
                // Filter the projection nodes for the actual type of the entity
                // The projection node might refer to the property in a derived type. If the TargetResourceType of
                // the projection node is not a super type, then we do not want to serialize this property.
                projectionNodes = projectionNodes.Where(projectionNode => projectionNode.TargetResourceType.IsAssignableFrom(actualResourceType));

                // Because we are going to enumerate through these multiple times, create a list.
                projectionNodes = projectionNodes.ToList();

                // And add the annotation to tell ODataLib which properties to write into content (the projections)
                entry.SetAnnotation(new ProjectedPropertiesAnnotation(projectionNodes.Select(p => p.PropertyName)));
            }

            // Populate the advertised actions
            IEnumerable <ODataAction> actions;
            if (this.TryGetAdvertisedActions(entityToSerialize, resourceInstanceInFeed, out actions))
            {
                foreach (ODataAction action in actions)
                {
                    entry.AddAction(action);
                }
            }

            // Populate all the normal properties
            entry.Properties = this.GetEntityProperties(entityToSerialize, projectionNodes);

            // And start the entry
            var args = new DataServiceODataWriterEntryArgs(entry, element, this.Service.OperationContext);
            this.dataServicesODataWriter.WriteStart(args);

            // Now write all the navigation properties
            this.WriteNavigationProperties(expanded, entityToSerialize, resourceInstanceInFeed, projectionNodes);

            // And write the end of the entry
            this.dataServicesODataWriter.WriteEnd(args);

#if ASTORIA_FF_CALLBACKS
            this.Service.InternalOnWriteItem(target, element);
#endif
        }
示例#25
0
        /// <summary>
        /// Gets the association link URL.
        /// </summary>
        /// <param name="entityToSerialize">The entity to serialize.</param>
        /// <param name="navigationProperty">The navigation property.</param>
        /// <returns>The association link url.</returns>
        private static Uri GetAssociationLinkUrl(EntityToSerialize entityToSerialize, ResourceProperty navigationProperty)
        {
            Uri associationLinkUri = RequestUriProcessor.AppendUnescapedSegment(entityToSerialize.SerializedKey.RelativeEditLink, navigationProperty.Name);

            return(RequestUriProcessor.AppendEscapedSegment(associationLinkUri, XmlConstants.UriLinkSegment));
        }
示例#26
0
        /// <summary>Gets ODataProperty for the given <paramref name="property"/>.</summary>
        /// <param name="entityToSerialize">Entity that is currently being serialized.</param>
        /// <param name="property">ResourceProperty instance in question.</param>
        /// <returns>A instance of ODataProperty for the given <paramref name="property"/>.</returns>
        private ODataProperty GetODataPropertyForEntityProperty(EntityToSerialize entityToSerialize, ResourceProperty property)
        {
            Debug.Assert(entityToSerialize != null, "entityToSerialize != null");
            Debug.Assert(property != null && entityToSerialize.ResourceType.Properties.Contains(property), "property != null && currentResourceType.Properties.Contains(property)");

            ODataValue odataPropertyValue;
            if (property.IsOfKind(ResourcePropertyKind.Stream))
            {
                odataPropertyValue = this.GetNamedStreamPropertyValue(entityToSerialize, property);
            }
            else
            {
                object propertyValue = WebUtil.GetPropertyValue(this.Provider, entityToSerialize.Entity, entityToSerialize.ResourceType, property, null);
                odataPropertyValue = this.GetPropertyValue(property.Name, property.ResourceType, propertyValue, false /*openProperty*/);
            }

            ODataProperty odataProperty = new ODataProperty()
            {
                Name = property.Name,
                Value = odataPropertyValue
            };

            ODataPropertyKind kind = property.IsOfKind(ResourcePropertyKind.Key) ? ODataPropertyKind.Key : property.IsOfKind(ResourcePropertyKind.ETag) ? ODataPropertyKind.ETag : ODataPropertyKind.Unspecified;
            if (kind != ODataPropertyKind.Unspecified)
            {
                odataProperty.SetSerializationInfo(new ODataPropertySerializationInfo { PropertyKind = kind });
            }

            return odataProperty;
        }
示例#27
0
        /// <summary>
        /// Tries to build the set of <see cref="ODataAction"/> to be advertised for the given resource.
        /// </summary>
        /// <param name="entityToSerialize">Entity that is currently being serialized.</param>
        /// <param name="resourceInstanceInFeed">true if the resource instance being serialized is inside a feed; false otherwise.</param>
        /// <param name="actions">The actions to advertise.</param>
        /// <returns>
        /// Whether any actions should be advertised.
        /// </returns>
        private bool TryGetAdvertisedActions(EntityToSerialize entityToSerialize, bool resourceInstanceInFeed, out IEnumerable<ODataAction> actions)
        {
            Debug.Assert(entityToSerialize != null, "entityToSerialize != null");

            if (!this.Service.ActionProvider.IsImplemented)
            {
                actions = null;
                return false;
            }

            // If GetOperationProjections returns null it means no projections are to be applied and all operations
            // for the current segment should be serialized. If it returns non-null only the operations described
            // by the returned projection segments should be serialized.
            ExpandedProjectionNode expandedProjectionNode = this.GetCurrentExpandedProjectionNode();

            List<OperationWrapper> serviceOperationWrapperList;
            if (expandedProjectionNode == null || expandedProjectionNode.ProjectAllOperations)
            {
                // Note that if the service does not implement IDataServiceActionProvider and the MaxProtocolVersion < V3, 
                // GetServiceActionsByBindingParameterType() would simply return an empty ServiceOperationWrapper collection.
                serviceOperationWrapperList = this.Service.ActionProvider.GetServiceActionsByBindingParameterType(entityToSerialize.ResourceType);
            }
            else
            {
                serviceOperationWrapperList = expandedProjectionNode.SelectedOperations.GetSelectedOperations(entityToSerialize.ResourceType);
            }

            Debug.Assert(serviceOperationWrapperList != null, "serviceOperationWrappers != null");
            if (serviceOperationWrapperList.Count <= 0)
            {
                actions = null;
                return false;
            }

            actions = this.OperationSerializer.SerializeOperations(entityToSerialize, resourceInstanceInFeed, serviceOperationWrapperList);
            return true;
        }
示例#28
0
        /// <summary>Gets ODataProperty for the given <paramref name="property"/>.</summary>
        /// <param name="entityToSerialize">Entity that is currently being serialized.</param>
        /// <param name="property">ResourceProperty instance in question.</param>
        /// <returns>A instance of ODataProperty for the given <paramref name="property"/>.</returns>
        private ODataNestedResourceInfoWrapper GetODataNestedResourceForComplexProperty(EntityToSerialize entityToSerialize, ResourceProperty property)
        {
            Debug.Assert(entityToSerialize != null, "entityToSerialize != null");
            Debug.Assert(property != null && entityToSerialize.ResourceType.Properties.Contains(property), "property != null && currentResourceType.Properties.Contains(property)");

            ODataItemWrapper odataPropertyResourceWrapper;
            object           propertyValue = WebUtil.GetPropertyValue(this.Provider, entityToSerialize.Entity, entityToSerialize.ResourceType, property, null);

            odataPropertyResourceWrapper = this.GetPropertyResourceOrResourceSet(property.Name, property.ResourceType, propertyValue, false /*openProperty*/);

            ODataNestedResourceInfo odataNestedInfo = new ODataNestedResourceInfo()
            {
                Name         = property.Name,
                IsCollection = property.Kind == ResourcePropertyKind.Collection,
            };

            odataNestedInfo.SetSerializationInfo(new ODataNestedResourceInfoSerializationInfo()
            {
                IsComplex = true
            });

            return(new ODataNestedResourceInfoWrapper()
            {
                NestedResourceInfo = odataNestedInfo,
                NestedResourceOrResourceSet = odataPropertyResourceWrapper
            });
        }
示例#29
0
        /// <summary>
        /// Asks the provider if the action should be advertised in payloads.
        /// </summary>
        /// <param name="entityToSerialize">The entity to serialize.</param>
        /// <param name="resourceInstanceInFeed">Whether or not the entity is being serialized in a feed.</param>
        /// <param name="serviceOperationWrapper">The service operation wrapper.</param>
        /// <param name="lazyActionTargetUri">Target uri of the action, which will only be generated if needed.</param>
        /// <param name="entityHasMultipleActionsWithSameName">Whether or not there are multiple operations in the current scope with the same name as the current operation.</param>
        /// <param name="odataAction">The ODL object-model representation of the action.</param>
        /// <returns>Whether or not the action should be advertised.</returns>
        private bool AskProviderIfActionShouldBeAdvertised(EntityToSerialize entityToSerialize, bool resourceInstanceInFeed, OperationWrapper serviceOperationWrapper, SimpleLazy<Uri> lazyActionTargetUri, bool entityHasMultipleActionsWithSameName, ref ODataAction odataAction)
        {
            if (this.advertiseServiceAction(serviceOperationWrapper, entityToSerialize.Entity, resourceInstanceInFeed, ref odataAction))
            {
                if (odataAction == null)
                {
                    throw new DataServiceException(500, Microsoft.OData.Service.Strings.DataServiceActionProviderWrapper_AdvertiseServiceActionCannotReturnNullActionToSerialize);
                }

                // Always set target and title if there are overloaded actions.
                if (!entityHasMultipleActionsWithSameName)
                {
                    this.metadataPropertyManager.CheckForUnmodifiedTitle(odataAction, serviceOperationWrapper.Name);
                    this.metadataPropertyManager.CheckForUnmodifiedTarget(odataAction, () => lazyActionTargetUri.Value);
                }

                // make the target link relative
                this.MakeOperationTargetRelativeFromMetadataUriIfJsonLight(odataAction);

                return true;
            }

            odataAction = null;
            return false;
        }
示例#30
0
        /// <summary>
        /// Tries to serialize the operation.
        /// </summary>
        /// <param name="entityToSerialize">The entity to serialize.</param>
        /// <param name="resourceInstanceInFeed">Whether or not the entity is being serialized in a feed.</param>
        /// <param name="entityHasMultipleActionsWithSameName">Whether or not there are multiple operations in the current scope with the same name as the current operation.</param>
        /// <param name="serviceOperationWrapper">The service operation wrapper.</param>
        /// <param name="odataAction">The ODL object-model representation of the action.</param>
        /// <returns>Whether or not to serialize the operation.</returns>
        private bool TrySerializeOperation(EntityToSerialize entityToSerialize, bool resourceInstanceInFeed, bool entityHasMultipleActionsWithSameName, OperationWrapper serviceOperationWrapper, out ODataAction odataAction)
        {
            Debug.Assert(serviceOperationWrapper != null, "serviceOperationWrapper != null");

            // We only advertise actions. This is a debug assert because GetServiceOperationsByResourceType only returns actions.
            Debug.Assert(serviceOperationWrapper.Kind == OperationKind.Action, "Only actions can be advertised");

            Uri metadata = this.operationLinkBuilder.BuildMetadataLink(serviceOperationWrapper, entityHasMultipleActionsWithSameName);

            // If the action has OperationParameterBindingKind set to "Always" then we advertise the action without calling "AdvertiseServiceAction".
            bool isAlwaysAvailable = serviceOperationWrapper.OperationParameterBindingKind == OperationParameterBindingKind.Always;

            odataAction = new ODataAction { Metadata = metadata };

            // There is some subtlety to the interaction between action advertisement and whether or not to include title/target on the wire.
            // 
            // 1) If an action is always available:
            //    The provider author does not get a chance to customize the title/target values...
            //    so the values will be based on conventions...
            //    so by default do not write them on the wire
            // 2) If it is only sometimes available:
            //    The values need to be computed to provide them on the instance given to the provider...
            //    but they might not be changed by the provider author...
            //    so compare them to the computed values, and do not write them if they match.
            
            // TODO: Action provider should be able to customize title/target even if the action is 'always' advertised
            // If this gets fixed, then all the behavior should collapse to emulate case #2 above

            // Create a lazy Uri for the target, because we may need it more than once (see case #2 above).
            SimpleLazy<Uri> lazyActionTargetUri = new SimpleLazy<Uri>(() => this.operationLinkBuilder.BuildTargetLink(entityToSerialize, serviceOperationWrapper, entityHasMultipleActionsWithSameName));

            this.metadataPropertyManager.SetTitle(odataAction, isAlwaysAvailable, serviceOperationWrapper.Name);
            this.metadataPropertyManager.SetTarget(odataAction, isAlwaysAvailable, () => lazyActionTargetUri.Value);

            // If the operation is always available,
            // 1. Return true for MetadataQueryOption.All.
            // 2. Return false for MetadataQueryOption.None.
            // 3. Return false for MetadataQueryOption.Default.
            if (isAlwaysAvailable)
            {
                return this.payloadMetadataParameterInterpreter.ShouldIncludeAlwaysAvailableOperation();
            }

            return this.AskProviderIfActionShouldBeAdvertised(entityToSerialize, resourceInstanceInFeed, serviceOperationWrapper, lazyActionTargetUri, entityHasMultipleActionsWithSameName, ref odataAction);
        }
示例#31
0
        /// <summary>
        /// Get the ODataStreamReferenceValue instance containing the metadata for named stream property.
        /// </summary>
        /// <param name="entityToSerialize">Entity that is currently being serialized.</param>
        /// <param name="namedStreamProperty">Named stream property for which the link element needs to be written.</param>
        /// <returns>
        /// An instance of ODataStreamReferenceValue containing all the metadata about the named stream property.
        /// </returns>
        private ODataStreamReferenceValue GetNamedStreamPropertyValue(EntityToSerialize entityToSerialize, ResourceProperty namedStreamProperty)
        {
            Debug.Assert(entityToSerialize != null, "entityToSerialize != null");
            Debug.Assert(namedStreamProperty != null, "namedStreamProperty != null");
            Debug.Assert(namedStreamProperty.IsOfKind(ResourcePropertyKind.Stream), "namedStreamProperty.IsOfKind(ResourcePropertyKind.Stream)");
            
            string mediaETag;
            Uri readStreamUri;
            string mediaContentType;
            this.Service.StreamProvider.GetStreamDescription(entityToSerialize.Entity, namedStreamProperty, this.Service.OperationContext, out mediaETag, out readStreamUri, out mediaContentType);
            Debug.Assert(WebUtil.IsETagValueValid(mediaETag, true), "WebUtil.IsETagValueValid(mediaETag, true)");

            ODataStreamReferenceValue streamReferenceValue = new ODataStreamReferenceValue();

            this.PayloadMetadataPropertyManager.SetContentType(streamReferenceValue, mediaContentType);

            this.PayloadMetadataPropertyManager.SetEditLink(
                streamReferenceValue,
                () =>
                {
                    Uri relativeUri = entityToSerialize.SerializedKey.RelativeEditLink;
                    return RequestUriProcessor.AppendUnescapedSegment(relativeUri, namedStreamProperty.Name);
                });

            if (!string.IsNullOrEmpty(mediaETag))
            {
                this.PayloadMetadataPropertyManager.SetETag(streamReferenceValue, mediaETag);
            }

            // Note if readStreamUri is null, the self link for the named stream will be omitted.
            this.PayloadMetadataPropertyManager.SetReadLink(streamReferenceValue, () => readStreamUri);
            return streamReferenceValue;
        }
示例#32
0
 /// <summary>
 /// Gets the association link URL.
 /// </summary>
 /// <param name="entityToSerialize">The entity to serialize.</param>
 /// <param name="navigationProperty">The navigation property.</param>
 /// <returns>The association link url.</returns>
 private static Uri GetAssociationLinkUrl(EntityToSerialize entityToSerialize, ResourceProperty navigationProperty)
 {
     Uri associationLinkUri = RequestUriProcessor.AppendUnescapedSegment(entityToSerialize.SerializedKey.RelativeEditLink, navigationProperty.Name);
     return RequestUriProcessor.AppendEscapedSegment(associationLinkUri, XmlConstants.UriLinkSegment);
 }
示例#33
0
        /// <summary>
        /// Get the stream reference value for media resource (the default stream of an entity).
        /// </summary>
        /// <param name="entityToSerialize">Entity that is currently being serialized.</param>
        /// <param name="title">The title for the element being written.</param>
        /// <returns>
        /// An instance of ODataStreamReferenceValue containing the metadata about the media resource.
        /// </returns>
        private ODataStreamReferenceValue GetMediaResource(EntityToSerialize entityToSerialize, string title)
        {
            Debug.Assert(entityToSerialize.Entity != null, "element != null");
            Debug.Assert(entityToSerialize.ResourceType != null && entityToSerialize.ResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "type != null && type.ResourceTypeKind == ResourceTypeKind.EntityType");
            Debug.Assert(!string.IsNullOrEmpty(title), "!string.IsNullOrEmpty(title)");

            ODataStreamReferenceValue mediaResource = null;

            // Handle MLE
            if (entityToSerialize.ResourceType.IsMediaLinkEntry)
            {
                string mediaETag;
                Uri readStreamUri;
                string mediaContentType;

                Debug.Assert(entityToSerialize.ResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "type.ResourceTypeKind == ResourceTypeKind.EntityType");
                this.Service.StreamProvider.GetStreamDescription(entityToSerialize.Entity, null /*null for MLE*/, this.Service.OperationContext, out mediaETag, out readStreamUri, out mediaContentType);
                Debug.Assert(WebUtil.IsETagValueValid(mediaETag, true), "WebUtil.IsETagValueValid(mediaETag, true)");
                Debug.Assert(!string.IsNullOrEmpty(mediaContentType), "!string.IsNullOrEmpty(mediaContentType)");

                mediaResource = new ODataStreamReferenceValue();

                // build the stream's edit link lazily to avoid creating the entity's edit link if it is not needed.
                SimpleLazy<Uri> lazyStreamEditLink = new SimpleLazy<Uri>(() => RequestUriProcessor.AppendEscapedSegment(entityToSerialize.SerializedKey.RelativeEditLink, XmlConstants.UriValueSegment));

                this.PayloadMetadataPropertyManager.SetEditLink(mediaResource, () => lazyStreamEditLink.Value);

                this.PayloadMetadataPropertyManager.SetContentType(mediaResource, mediaContentType);

                // If the stream provider did not provider a read link, then we should use the edit link as the read link.
                this.PayloadMetadataPropertyManager.SetReadLink(mediaResource, () => readStreamUri ?? lazyStreamEditLink.Value);
#pragma warning disable 618
                if (this.contentFormat == ODataFormat.Atom)
#pragma warning restore 618
                {
                    AtomStreamReferenceMetadata mediaResourceAtom = new AtomStreamReferenceMetadata() { EditLink = new AtomLinkMetadata { Title = title } };
                    mediaResource.SetAnnotation(mediaResourceAtom);
                }

                if (!string.IsNullOrEmpty(mediaETag))
                {
                    this.PayloadMetadataPropertyManager.SetETag(mediaResource, mediaETag);
                }
            }

            return mediaResource;
        }
示例#34
0
 /// <summary>
 /// Wraps the entity in a structure which tracks its type and other information about it.
 /// </summary>
 /// <param name="entity">The entity to wrap.</param>
 /// <param name="resourceType">The type of the entity.</param>
 /// <returns>A structure containing the entity and some other information about it.</returns>
 private EntityToSerialize WrapEntity(object entity, ResourceType resourceType)
 {
     return(EntityToSerialize.Create(entity, resourceType, this.CurrentContainer, this.Provider, this.AbsoluteServiceUri));
 }
示例#35
0
        /// <summary>
        /// Serializes the given operations and returns the resulting instances of <see cref="ODataAction"/>.
        /// </summary>
        /// <param name="entityToSerialize">The entity to serialize.</param>
        /// <param name="resourceInstanceInFeed">Whether or not the entity is being serialized in a feed.</param>
        /// <param name="operationWrappers">The wrapped operations to serialize.</param>
        /// <returns>The serialized actions.</returns>
        internal IEnumerable<ODataAction> SerializeOperations(EntityToSerialize entityToSerialize, bool resourceInstanceInFeed, ICollection<OperationWrapper> operationWrappers)
        {
            Debug.Assert(operationWrappers != null, "serviceOperationWrapperList != null");

            var odataActions = new List<ODataAction>(operationWrappers.Count);
            
            // Create a hashset containing the names of all actions in the current scope that are duplicates.
            HashSet<string> collidingActionNames = GetCollidingActionNames(operationWrappers);
            foreach (OperationWrapper wrapper in operationWrappers)
            {
                bool actionNameHasCollision = collidingActionNames.Contains(wrapper.Name);

                ODataAction odataAction;
                if (this.TrySerializeOperation(entityToSerialize, resourceInstanceInFeed, actionNameHasCollision, wrapper, out odataAction))
                {
                    odataActions.Add(odataAction);
                }
            }

            return odataActions;
        }