protected override void WriteTopLevelElements(IExpandedResult expanded, IEnumerator elements, bool hasMoved)
 {
     if (base.RequestDescription.LinkUri)
     {
         bool needPop = base.PushSegmentForRoot();
         this.WriteLinkCollection(elements, hasMoved);
         base.PopSegmentName(needPop);
     }
     else
     {
         this.collectionWriter = this.writer.CreateODataCollectionWriter();
         ODataCollectionStart collectionStart = new ODataCollectionStart {
             Name = this.ComputeContainerName()
         };
         this.collectionWriter.WriteStart(collectionStart);
         while (hasMoved)
         {
             object       current = elements.Current;
             ResourceType propertyResourceType = (current == null) ? base.RequestDescription.TargetResourceType : WebUtil.GetResourceType(base.Provider, current);
             if (propertyResourceType == null)
             {
                 throw new InvalidOperationException(System.Data.Services.Strings.Serializer_UnsupportedTopLevelType(current.GetType()));
             }
             this.collectionWriter.WriteItem(base.GetPropertyValue("element", propertyResourceType, current, false));
             hasMoved = elements.MoveNext();
         }
         this.collectionWriter.WriteEnd();
         this.collectionWriter.Flush();
     }
 }
        protected override void WriteTopLevelElement(IExpandedResult expandedResult, object element)
        {
            string propertyName = this.ComputeContainerName();

            if (base.RequestDescription.LinkUri)
            {
                bool needPop = base.PushSegmentForRoot();
                this.WriteLink(element);
                base.PopSegmentName(needPop);
            }
            else
            {
                ResourceType type;
                if (element == null)
                {
                    type = (base.RequestDescription.TargetKind == RequestTargetKind.OpenProperty) ? ResourceType.PrimitiveStringResourceType : base.RequestDescription.TargetResourceType;
                }
                else
                {
                    type = (base.RequestDescription.TargetKind == RequestTargetKind.Collection) ? base.RequestDescription.TargetResourceType : WebUtil.GetResourceType(base.Provider, element);
                }
                if (type == null)
                {
                    throw new InvalidOperationException(System.Data.Services.Strings.Serializer_UnsupportedTopLevelType(element.GetType()));
                }
                ODataProperty property = new ODataProperty {
                    Name  = propertyName,
                    Value = base.GetPropertyValue(propertyName, type, element, false)
                };
                this.writer.WriteProperty(property);
            }
        }
        /// <summary>
        /// Return the collection of links as ODataEntityReferenceLink instances
        /// </summary>
        /// <param name="elements">Elements whose uri need to be written out.</param>
        /// <param name="linksCollection">LinkCollection instance which represents the collection getting written out.</param>
        /// <returns>Return the collection of links as ODataEntityReferenceLink instances.</returns>
        private IEnumerable <ODataEntityReferenceLink> GetLinksCollection(QueryResultInfo elements, ODataEntityReferenceLinks linksCollection)
        {
            object          lastObject            = null;
            IExpandedResult lastExpandedSkipToken = null;

            while (elements.HasMoved)
            {
                object          element   = elements.Current;
                IExpandedResult skipToken = null;
                if (element != null)
                {
                    IExpandedResult expanded = element as IExpandedResult;
                    if (expanded != null)
                    {
                        element   = GetExpandedElement(expanded);
                        skipToken = this.GetSkipToken(expanded);
                    }
                }

                this.IncrementSegmentResultCount();
                yield return(new ODataEntityReferenceLink {
                    Url = this.GetEntityEditLink(element)
                });

                elements.MoveNext();
                lastObject            = element;
                lastExpandedSkipToken = skipToken;
            }

            if (this.NeedNextPageLink(elements))
            {
                linksCollection.NextPageLink = this.GetNextLinkUri(lastObject, lastExpandedSkipToken, this.RequestDescription.ResultUri);
                yield break;
            }
        }
        private IEnumerable <ODataEntityReferenceLink> GetLinksCollection(IEnumerator elements, bool hasMoved, ODataEntityReferenceLinks linksCollection)
        {
            object          lastObject = null;
            IExpandedResult skipTokenExpandedResult = null;

            Label_PostSwitchInIterator :;
            if (hasMoved)
            {
                object          current   = elements.Current;
                IExpandedResult skipToken = null;
                if (current != null)
                {
                    IExpandedResult expanded = current as IExpandedResult;
                    if (expanded != null)
                    {
                        current   = Serializer.GetExpandedElement(expanded);
                        skipToken = this.GetSkipToken(expanded);
                    }
                }
                this.IncrementSegmentResultCount();
                ODataEntityReferenceLink iteratorVariable4 = new ODataEntityReferenceLink {
                    Url = this.GetEntityUri(current)
                };
                yield return(iteratorVariable4);

                hasMoved   = elements.MoveNext();
                lastObject = current;
                skipTokenExpandedResult = skipToken;
                goto Label_PostSwitchInIterator;
            }
            if (this.NeedNextPageLink(elements))
            {
                linksCollection.NextPageLink = this.GetNextLinkUri(lastObject, skipTokenExpandedResult, this.RequestDescription.ResultUri);
            }
        }
Beispiel #5
0
        /// <summary>Writes a single top-level element.</summary>
        /// <param name="expandedResult">Expandd results on the specified <paramref name="element"/>.</param>
        /// <param name="element">Element to write, possibly null.</param>
        protected override void WriteTopLevelElement(IExpandedResult expandedResult, object element)
        {
            Debug.Assert(
                element != null || this.RequestDescription.TargetResourceType != null || this.RequestDescription.TargetKind == RequestTargetKind.OpenProperty,
                "element != null || this.RequestDescription.TargetResourceType != null || this.RequestDescription.TargetKind == RequestTargetKind.OpenProperty");
            Debug.Assert(
                this.RequestDescription.IsSingleResult,
                "this.RequestDescription.IsSingleResult -- primitive collections not currently supported");
            string       propertyName = this.RequestDescription.ContainerName;
            ResourceType resourceType;

            if (element == null)
            {
                if (this.RequestDescription.TargetKind == RequestTargetKind.OpenProperty)
                {
                    resourceType = ResourceType.PrimitiveStringResourceType;
                }
                else
                {
                    resourceType = this.RequestDescription.TargetResourceType;
                }
            }
            else
            {
                resourceType = WebUtil.GetResourceType(this.Provider, element);
            }

            if (resourceType == null)
            {
                throw new InvalidOperationException(Strings.Serializer_UnsupportedTopLevelType(element.GetType()));
            }

            this.WriteValueWithName(element, propertyName, resourceType);
        }
Beispiel #6
0
        /// <summary>Writes multiple top-level elements, possibly none.</summary>
        /// <param name="expanded">Expanded properties for the result.</param>
        /// <param name="elements">Enumerator for elements to write.</param>
        protected override void WriteTopLevelElements(IExpandedResult expanded, QueryResultInfo elements)
        {
            Debug.Assert(elements != null, "elements != null");
            Debug.Assert(!this.RequestDescription.IsSingleResult, "!this.RequestDescription.SingleResult");

            string title;

            if (this.RequestDescription.TargetKind != RequestTargetKind.OpenProperty &&
                this.RequestDescription.TargetSource == RequestTargetSource.Property)
            {
                title = this.RequestDescription.Property.Name;
            }
            else
            {
                title = this.RequestDescription.ContainerName;
            }

            this.dataServicesODataWriter = this.CreateODataWriter(true /*forFeed*/);

            bool needPop = this.PushSegmentForRoot();

            this.WriteFeedElements(
                expanded,
                elements,
                this.RequestDescription.TargetResourceType.ElementType(),
                title,                                      // title
                () => new Uri(this.RequestDescription.LastSegmentInfo.Identifier, UriKind.Relative),
                () => this.RequestDescription.ResultUri,    // absoluteUri
                true);

            this.PopSegmentName(needPop);
        }
Beispiel #7
0
        /// <summary>Writes a single open property of a resource or complex object.</summary>
        /// <param name="propertyName">The name of the property to write.</param>
        /// <param name="propertyValue">The value of the property to write.</param>
        /// <param name="parentUri">Uri of the object whose properties are getting written.</param>
        private void WriteObjectOpenProperty(string propertyName, object propertyValue, Uri parentUri)
        {
            IExpandedResult expandedValue = propertyValue as IExpandedResult;

            if (expandedValue != null)
            {
                propertyValue = GetExpandedElement(expandedValue);
            }

            ResourceType propertyResourceType;

            if (propertyValue == null || propertyValue == DBNull.Value)
            {
                propertyValue        = null;
                propertyResourceType = ResourceType.PrimitiveStringResourceType;
            }
            else
            {
                // We need to ensure that strings and other primitive types which implement IEnumerable
                // are not considered as containing elements.
                propertyResourceType = WebUtil.GetResourceType(this.Provider, propertyValue);
                if (propertyResourceType == null)
                {
                    // Black-listed types are not supported.
                    return;
                }
            }

            this.writer.WriteName(propertyName);

            bool needPop = this.PushSegmentForOpenProperty(propertyName, propertyResourceType);

            this.WriteElementWithName(expandedValue, propertyValue, propertyName, Serializer.AppendEntryToUri(parentUri, propertyName), false /*topLevel*/);
            this.PopSegmentName(needPop);
        }
Beispiel #8
0
        /// <summary>
        /// Write out the uri for the given elements.
        /// </summary>
        /// <param name="elements">elements whose uri need to be writtne out</param>
        /// <param name="hasMoved">the current state of the enumerator.</param>
        /// <remarks>This method accounts for each link as a written entity on the current segment.</remarks>
        protected override void WriteLinkCollection(IEnumerator elements, bool hasMoved)
        {
            this.writer.StartObjectScope();
            this.writer.WriteDataWrapper();

            if (this.JsonFormatVersion >= 2)
            {
                // see comments in WriteTopLevelElements
                this.writer.StartObjectScope();
                this.writer.WriteDataArrayName();
            }

            this.writer.StartArrayScope();

            object          lastObject            = null;
            IExpandedResult lastExpandedSkipToken = null;

            while (hasMoved)
            {
                object          o         = elements.Current;
                IExpandedResult skipToken = null;
                if (o != null)
                {
                    IExpandedResult expanded = o as IExpandedResult;
                    if (expanded != null)
                    {
                        o         = GetExpandedElement(expanded);
                        skipToken = this.GetSkipToken(expanded);
                    }

                    Uri uri = Serializer.GetUri(o, this.Provider, this.CurrentContainer, this.AbsoluteServiceUri);
                    this.WriteLinkObject(uri);
                }

                hasMoved              = elements.MoveNext();
                lastObject            = o;
                lastExpandedSkipToken = skipToken;
            }

            this.writer.EndScope();

            if (this.JsonFormatVersion >= 2)
            {
                // $count=inline support
                if (this.RequestDescription.CountOption == RequestQueryCountOption.Inline)
                {
                    this.WriteRowCount();
                }

                if (this.NeedNextPageLink(elements))
                {
                    this.WriteNextPageLink(lastObject, lastExpandedSkipToken, this.RequestDescription.ResultUri);
                }

                this.writer.EndScope();
            }

            this.writer.EndScope();
        }
Beispiel #9
0
 protected IExpandedResult GetSkipToken(IExpandedResult expanded)
 {
     if (((expanded != null) && !this.IsCustomPaged) && !this.RequestDescription.IsRequestForEnumServiceOperation)
     {
         return(expanded.GetExpandedPropertyValue("$skiptoken") as IExpandedResult);
     }
     return(null);
 }
Beispiel #10
0
        /// <summary>Writes all the properties of the specified resource.</summary>
        /// <param name="expanded">Expanded properties for the result.</param>
        /// <param name="resource">Resource with properties to write out.</param>
        /// <param name="resourceType">Type for the specified resource (saves the lookup in this method).</param>
        /// <param name="uri">uri of the resource whose properties are getting written</param>
        private void WriteResourceProperties(IExpandedResult expanded, object resource, ResourceType resourceType, Uri uri)
        {
            Debug.Assert(resource != null, "resource != null");
            Debug.Assert(resourceType != null, "resourceType != null");
            Debug.Assert(resourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "resource must be entity type");
            Debug.Assert(uri != null, "uri != null");

            this.WriteObjectProperties(expanded, resource, resourceType, uri, true);
        }
Beispiel #11
0
        /// <summary>Writes a single top-level element.</summary>
        /// <param name="expanded">Expanded properties for the result.</param>
        /// <param name="element">Element to write, possibly null.</param>
        protected override void WriteTopLevelElement(IExpandedResult expanded, object element)
        {
            Debug.Assert(this.RequestDescription.IsSingleResult, "this.RequestDescription.SingleResult");

            bool needPop = this.PushSegmentForRoot();

            this.writer.StartObjectScope(); // {
            this.writer.WriteDataWrapper(); // "d" :
            this.WriteElementWithName(expanded, element, this.RequestDescription.ContainerName, this.RequestDescription.ResultUri, true /*topLevel*/);
            this.writer.EndScope();         // }
            this.PopSegmentName(needPop);
        }
Beispiel #12
0
        private void WriteEntry(IExpandedResult expanded, object element, bool resourceInstanceInFeed, ResourceType expectedType)
        {
            Uri uri;
            Func <ProjectionNode, bool> predicate = null;

            base.IncrementSegmentResultCount();
            ODataEntry        entry      = new ODataEntry();
            AtomEntryMetadata annotation = new AtomEntryMetadata();

            entry.SetAnnotation <AtomEntryMetadata>(annotation);
            string       name = expectedType.Name;
            ResourceType actualResourceType = WebUtil.GetNonPrimitiveResourceType(base.Provider, element);

            if (actualResourceType.ResourceTypeKind != ResourceTypeKind.EntityType)
            {
                throw new DataServiceException(500, System.Data.Services.Strings.BadProvider_InconsistentEntityOrComplexTypeUsage(actualResourceType.FullName));
            }
            Uri absoluteUri = Serializer.GetIdAndEditLink(element, actualResourceType, base.Provider, base.CurrentContainer, base.AbsoluteServiceUri, out uri);
            Uri relativeUri = new Uri(absoluteUri.AbsoluteUri.Substring(base.AbsoluteServiceUri.AbsoluteUri.Length), UriKind.Relative);

            entry.MediaResource = this.GetMediaResource(element, actualResourceType, name, relativeUri);
            entry.TypeName      = actualResourceType.FullName;
            entry.Id            = uri.AbsoluteUri;
            entry.EditLink      = relativeUri;
            AtomLinkMetadata metadata2 = new AtomLinkMetadata {
                Title = name
            };

            annotation.EditLink = metadata2;
            string eTagValue = base.GetETagValue(element, actualResourceType);

            if (eTagValue != null)
            {
                entry.ETag = eTagValue;
            }
            IEnumerable <ProjectionNode> projections = base.GetProjections();

            if (projections != null)
            {
                if (predicate == null)
                {
                    predicate = projectionNode => projectionNode.TargetResourceType.IsAssignableFrom(actualResourceType);
                }
                projections = projections.Where <ProjectionNode>(predicate);
                entry.SetAnnotation <ProjectedPropertiesAnnotation>(new ProjectedPropertiesAnnotation(from p in projections select p.PropertyName));
            }
            entry.AssociationLinks = this.GetEntityAssociationLinks(actualResourceType, relativeUri, projections);
            this.PopulateODataOperations(element, resourceInstanceInFeed, entry, actualResourceType);
            this.odataWriter.WriteStart(entry);
            this.WriteNavigationProperties(expanded, element, resourceInstanceInFeed, actualResourceType, absoluteUri, relativeUri, projections);
            entry.Properties = this.GetEntityProperties(element, actualResourceType, relativeUri, projections);
            this.odataWriter.WriteEnd();
        }
Beispiel #13
0
        protected ResourcePropertyInfo GetNavigationPropertyInfo(IExpandedResult expanded, object customObject, ResourceType currentResourceType, ResourceProperty property)
        {
            ExpandedProjectionNode node;
            object obj2   = null;
            bool   expand = this.ShouldExpandSegment(property, currentResourceType, out node);

            if (expand)
            {
                obj2 = this.GetExpandedProperty(expanded, customObject, property, node);
            }
            return(ResourcePropertyInfo.CreateResourcePropertyInfo(property, obj2, node, expand));
        }
        /// <summary>Writes multiple top-level elements, possibly none.</summary>
        /// <param name="expanded">Expanded results for elements.</param>
        /// <param name="elements">Enumerator for elements to write.</param>
        protected override void WriteTopLevelElements(IExpandedResult expanded, QueryResultInfo elements)
        {
            Debug.Assert(
                !this.RequestDescription.IsSingleResult,
                "!this.RequestDescription.IsSingleResult -- otherwise WriteTopLevelElement should have been called");

            if (this.RequestDescription.LinkUri)
            {
                bool needPop = this.PushSegmentForRoot();
                this.WriteLinkCollection(elements);
                this.PopSegmentName(needPop);
            }
            else
            {
                MetadataProviderEdmModel model     = this.Service.Provider.GetMetadataProviderEdmModel();
                OperationWrapper         operation = this.RequestDescription.LastSegmentInfo.Operation;

                IEdmOperation edmOperation = model.GetRelatedOperation(operation);
                Debug.Assert(edmOperation != null, "edmOperation != null");

                IEdmCollectionTypeReference collectionType = (IEdmCollectionTypeReference)edmOperation.ReturnType;
                bool isJsonLightResponse = ContentTypeUtil.IsResponseMediaTypeJsonLight(this.Service, /*isEntryOrFeed*/ false);
                this.collectionWriter = this.writer.CreateODataCollectionWriter(isJsonLightResponse ? null : collectionType.ElementType());

                ODataCollectionStart collectionStart = new ODataCollectionStart {
                    Name = this.ComputeContainerName()
                };
                collectionStart.SetSerializationInfo(new ODataCollectionStartSerializationInfo {
                    CollectionTypeName = collectionType.FullName()
                });

                this.collectionWriter.WriteStart(collectionStart);
                while (elements.HasMoved)
                {
                    object       element      = elements.Current;
                    ResourceType resourceType = element == null ?
                                                this.RequestDescription.TargetResourceType : WebUtil.GetResourceType(this.Provider, element);
                    if (resourceType == null)
                    {
                        throw new InvalidOperationException(Microsoft.OData.Service.Strings.Serializer_UnsupportedTopLevelType(element.GetType()));
                    }

                    this.collectionWriter.WriteItem(this.GetPropertyValue(XmlConstants.XmlCollectionItemElementName, resourceType, element, false /*openProperty*/).FromODataValue());
                    elements.MoveNext();
                }

                this.collectionWriter.WriteEnd();
                this.collectionWriter.Flush();
            }
        }
Beispiel #15
0
        protected object GetExpandedProperty(IExpandedResult expanded, object customObject, ResourceProperty property, ExpandedProjectionNode expandedNode)
        {
            if (expanded == null)
            {
                return(WebUtil.GetPropertyValue(this.Provider, customObject, null, property, null));
            }
            string name = property.Name;

            if ((expandedNode != null) && (this.GetCurrentExpandedProjectionNode().ResourceType != expandedNode.TargetResourceType))
            {
                name = expandedNode.TargetResourceType.FullName + "/" + property.Name;
            }
            return(expanded.GetExpandedPropertyValue(name));
        }
        /// <summary>Writes a single top-level element.</summary>
        /// <param name="expandedResult">Expandd results on the specified <paramref name="element"/>.</param>
        /// <param name="element">Element to write, possibly null.</param>
        protected override void WriteTopLevelElement(IExpandedResult expandedResult, object element)
        {
            Debug.Assert(
                element != null || this.RequestDescription.TargetResourceType != null || this.RequestDescription.TargetKind == RequestTargetKind.OpenProperty,
                "element != null || this.RequestDescription.TargetResourceType != null || this.RequestDescription.TargetKind == RequestTargetKind.OpenProperty");
            Debug.Assert(
                this.RequestDescription.IsSingleResult,
                "this.RequestDescription.IsSingleResult -- primitive collections not currently supported");

            string propertyName = this.ComputeContainerName();

            if (this.RequestDescription.LinkUri)
            {
                bool needPop = this.PushSegmentForRoot();
                this.WriteLink(element);
                this.PopSegmentName(needPop);
            }
            else
            {
                ResourceType resourceType;
                if (element == null)
                {
                    resourceType = this.RequestDescription.TargetKind == RequestTargetKind.OpenProperty
                        ? ResourceType.PrimitiveStringResourceType
                        : this.RequestDescription.TargetResourceType;
                }
                else
                {
                    resourceType = this.RequestDescription.TargetKind == RequestTargetKind.Collection
                        ? this.RequestDescription.TargetResourceType
                        : WebUtil.GetResourceType(this.Provider, element);
                }

                if (resourceType == null)
                {
                    throw new InvalidOperationException(Microsoft.OData.Service.Strings.Serializer_UnsupportedTopLevelType(element.GetType()));
                }

                var odataProperty = new ODataProperty
                {
                    Name = propertyName,
                    Value = this.GetPropertyValue(propertyName, resourceType, element, this.RequestDescription.TargetKind == RequestTargetKind.OpenProperty /*openProperty*/)
                };

                this.writer.WriteProperty(odataProperty);
            }
        }
        /// <summary>Writes a single top-level element.</summary>
        /// <param name="expandedResult">Expandd results on the specified <paramref name="element"/>.</param>
        /// <param name="element">Element to write, possibly null.</param>
        protected override void WriteTopLevelElement(IExpandedResult expandedResult, object element)
        {
            Debug.Assert(
                element != null || this.RequestDescription.TargetResourceType != null || this.RequestDescription.TargetKind == RequestTargetKind.OpenProperty,
                "element != null || this.RequestDescription.TargetResourceType != null || this.RequestDescription.TargetKind == RequestTargetKind.OpenProperty");
            Debug.Assert(
                this.RequestDescription.IsSingleResult,
                "this.RequestDescription.IsSingleResult -- primitive collections not currently supported");

            string propertyName = this.ComputeContainerName();

            if (this.RequestDescription.LinkUri)
            {
                bool needPop = this.PushSegmentForRoot();
                this.WriteLink(element);
                this.PopSegmentName(needPop);
            }
            else
            {
                ResourceType resourceType;
                if (element == null)
                {
                    resourceType = this.RequestDescription.TargetKind == RequestTargetKind.OpenProperty
                        ? ResourceType.PrimitiveStringResourceType
                        : this.RequestDescription.TargetResourceType;
                }
                else
                {
                    resourceType = this.RequestDescription.TargetKind == RequestTargetKind.Collection
                        ? this.RequestDescription.TargetResourceType
                        : WebUtil.GetResourceType(this.Provider, element);
                }

                if (resourceType == null)
                {
                    throw new InvalidOperationException(Microsoft.OData.Service.Strings.Serializer_UnsupportedTopLevelType(element.GetType()));
                }

                var odataProperty = new ODataProperty
                {
                    Name  = propertyName,
                    Value = this.GetPropertyValue(propertyName, resourceType, element, this.RequestDescription.TargetKind == RequestTargetKind.OpenProperty /*openProperty*/)
                };

                this.writer.WriteProperty(odataProperty);
            }
        }
Beispiel #18
0
        internal void WriteRequest(IEnumerator queryResults, bool hasMoved)
        {
            IExpandedResult expanded = queryResults as IExpandedResult;

            if (this.requestDescription.IsSingleResult)
            {
                this.WriteTopLevelElement(expanded, queryResults.Current);
                if (queryResults.MoveNext())
                {
                    throw new InvalidOperationException(System.Data.Services.Strings.SingleResourceExpected);
                }
            }
            else
            {
                this.WriteTopLevelElements(expanded, queryResults, hasMoved);
            }
        }
Beispiel #19
0
        protected override void WriteTopLevelElements(IExpandedResult expanded, IEnumerator elements, bool hasMoved)
        {
            string name;

            if ((base.RequestDescription.TargetKind != RequestTargetKind.OpenProperty) && (base.RequestDescription.TargetSource == RequestTargetSource.Property))
            {
                name = base.RequestDescription.Property.Name;
            }
            else
            {
                name = base.RequestDescription.ContainerName;
            }
            this.odataWriter = this.messageWriter.CreateODataFeedWriter();
            bool needPop = base.PushSegmentForRoot();

            this.WriteFeedElements(expanded, elements, base.RequestDescription.TargetResourceType.ElementType(), name, new Uri(base.RequestDescription.LastSegmentInfo.Identifier, UriKind.Relative), base.RequestDescription.ResultUri, hasMoved, true);
            base.PopSegmentName(needPop);
        }
Beispiel #20
0
        protected override void WriteTopLevelElement(IExpandedResult expanded, object element)
        {
            ResourceType targetResourceType;

            this.odataWriter = this.messageWriter.CreateODataEntryWriter();
            if ((base.RequestDescription.TargetSource == RequestTargetSource.EntitySet) || (base.RequestDescription.TargetSource == RequestTargetSource.ServiceOperation))
            {
                targetResourceType = base.RequestDescription.TargetResourceType;
            }
            else
            {
                targetResourceType = base.RequestDescription.Property.ResourceType;
            }
            bool needPop = base.PushSegmentForRoot();

            this.WriteEntry(expanded, element, false, targetResourceType);
            base.PopSegmentName(needPop);
        }
Beispiel #21
0
        /// <summary>Writes a single declared property 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 property to write out.</param>
        /// <param name="property">The resource property to write out.</param>
        /// <param name="parentUri">Uri of the object whose properties are getting written.</param>
        private void WriteObjectDeclaredProperty(IExpandedResult expanded, object customObject, ResourceProperty property, Uri parentUri)
        {
            string propertyName = property.Name;

            this.writer.WriteName(propertyName);

            // For any navigation property, we just stick the deferred element with the uri
            // This uri is different from the canonical uri: we just append the property name
            // to the parent uri. We don't want to analyze the nav property value in either case
            bool mayDefer = property.TypeKind == ResourceTypeKind.EntityType;

            if (mayDefer && !this.ShouldExpandSegment(propertyName))
            {
                this.WriteDeferredContentElement(Serializer.AppendEntryToUri(parentUri, propertyName));
            }
            else
            {
                object          propertyValue;
                IExpandedResult expandedValue = null;
                if (mayDefer)
                {
                    propertyValue = GetExpandedProperty(this.Provider, expanded, customObject, property);
                    expandedValue = propertyValue as IExpandedResult;
                    if (expandedValue != null)
                    {
                        propertyValue = GetExpandedElement(expandedValue);
                    }
                }
                else
                {
                    propertyValue = GetExpandedProperty(this.Provider, null, customObject, property);
                }

                if (propertyValue == null || propertyValue == DBNull.Value)
                {
                    propertyValue = null;
                }

                bool needPop = this.PushSegmentForProperty(property);
                this.WriteElementWithName(expandedValue, propertyValue, propertyName, Serializer.AppendEntryToUri(parentUri, propertyName), false /*topLevel*/);
                this.PopSegmentName(needPop);
            }
        }
Beispiel #22
0
        /// <summary>
        /// Write out the uri for the given elements
        /// </summary>
        /// <param name="elements">elements whose uri need to be writtne out</param>
        /// <param name="hasMoved">the current state of the enumerator.</param>
        protected override void WriteLinkCollection(IEnumerator elements, bool hasMoved)
        {
            this.writer.WriteStartElement(XmlConstants.LinkCollectionElementName, XmlConstants.DataWebNamespace);

            // write count?
            if (this.RequestDescription.CountOption == RequestQueryCountOption.Inline)
            {
                this.WriteRowCount();
            }

            object          lastObject            = null;
            IExpandedResult lastExpandedSkipToken = null;

            while (hasMoved)
            {
                object          element   = elements.Current;
                IExpandedResult skipToken = null;
                if (element != null)
                {
                    IExpandedResult expanded = element as IExpandedResult;
                    if (expanded != null)
                    {
                        element   = GetExpandedElement(expanded);
                        skipToken = this.GetSkipToken(expanded);
                    }
                }

                this.WriteLink(element);
                hasMoved              = elements.MoveNext();
                lastObject            = element;
                lastExpandedSkipToken = skipToken;
            }

            if (this.NeedNextPageLink(elements))
            {
                this.WriteNextPageLink(lastObject, lastExpandedSkipToken, this.RequestDescription.ResultUri);
            }

            this.writer.WriteEndElement();
        }
        public object[] GetSkipToken(IDataServiceQueryProvider dataServiceQueryPovider)
        {
            if (this.lastObject != null)
            {
                if (this.lastObject.GetType() == typeof(int))
                {
                    return(new object[] { -1 });
                }

                object          resource       = this.lastObject;
                IExpandedResult expandedResult = resource as IExpandedResult;
                if (expandedResult != null)
                {
                    resource = expandedResult.ExpandedElement;
                }

                IEnumerable <string> keyPropertyNames = this.IsReflectableResourceType ?
                                                        new string[] { "ID" } :
                this.resourceType.KeyProperties.Select(property => property.Name);

                ProjectedWrapper projectedWrapper = resource as ProjectedWrapper;
                if (projectedWrapper != null)
                {
                    return(keyPropertyNames.Select(propertyName =>
                                                   projectedWrapper.GetProjectedPropertyValue(propertyName)).ToArray());
                }
                else if (dataServiceQueryPovider != null)
                {
                    return(keyPropertyNames.Select(propertyName =>
                                                   dataServiceQueryPovider.GetPropertyValue(resource, this.resourceType.Properties.Single(p => p.Name == propertyName))).ToArray());
                }
                else
                {
                    return(keyPropertyNames.Select(propertyName =>
                                                   resource.GetType().GetProperty(propertyName).GetValue(resource, null)).ToArray());
                }
            }

            return(new Random().Next() % 2 == 0 ? null : new object[0]);
        }
Beispiel #24
0
        /// <summary>Writes multiple top-level elements, possibly none.</summary>
        /// <param name="expanded">Expanded results for elements.</param>
        /// <param name="elements">Enumerator for elements to write.</param>
        /// <param name="hasMoved">Whether <paramref name="elements"/> was succesfully advanced to the first element.</param>
        protected override void WriteTopLevelElements(IExpandedResult expanded, IEnumerator elements, bool hasMoved)
        {
            Debug.Assert(
                !this.RequestDescription.IsSingleResult,
                "!this.RequestDescription.IsSingleResult -- otherwise WriteTopLevelElement should have been called");
            this.writer.WriteStartElement(this.RequestDescription.ContainerName, XmlConstants.DataWebNamespace);
            while (hasMoved)
            {
                object       element      = elements.Current;
                ResourceType resourceType = element == null ?
                                            this.RequestDescription.TargetResourceType : WebUtil.GetResourceType(this.Provider, element);
                if (resourceType == null)
                {
                    throw new InvalidOperationException(Strings.Serializer_UnsupportedTopLevelType(element.GetType()));
                }

                this.WriteValueWithName(element, XmlConstants.XmlCollectionItemElementName, resourceType);
                hasMoved = elements.MoveNext();
            }

            this.writer.WriteEndElement();
        }
Beispiel #25
0
        protected Uri GetNextLinkUri(object lastObject, IExpandedResult skipTokenExpandedResult, Uri absoluteUri)
        {
            SkipTokenBuilder builder2;
            UriBuilder       builder = new UriBuilder(absoluteUri);

            if (this.IsRootContainer)
            {
                if (!this.IsCustomPaged)
                {
                    if (skipTokenExpandedResult != null)
                    {
                        builder2 = new SkipTokenBuilderFromExpandedResult(skipTokenExpandedResult, this.RequestDescription.SkipTokenExpressionCount);
                    }
                    else
                    {
                        builder2 = new SkipTokenBuilderFromProperties(lastObject, this.Provider, this.RequestDescription.SkipTokenProperties);
                    }
                }
                else
                {
                    builder2 = new SkipTokenBuilderFromCustomPaging(this.currentSkipTokenForCustomPaging);
                }
                builder.Query = this.GetNextPageQueryParametersForRootContainer().Append(builder2.GetSkipToken()).ToString();
            }
            else
            {
                if (!this.IsCustomPaged)
                {
                    builder2 = new SkipTokenBuilderFromProperties(lastObject, this.Provider, this.CurrentContainer.ResourceType.KeyProperties);
                }
                else
                {
                    builder2 = new SkipTokenBuilderFromCustomPaging(this.currentSkipTokenForCustomPaging);
                }
                builder.Query = this.GetNextPageQueryParametersForExpandedContainer().Append(builder2.GetSkipToken()).ToString();
            }
            return(builder.Uri);
        }
Beispiel #26
0
        /// <summary>Writes a single top-level element.</summary>
        /// <param name="expanded">Expanded properties for the result.</param>
        /// <param name="element">Element to write, possibly null.</param>
        protected override void WriteTopLevelElement(IExpandedResult expanded, object element)
        {
            Debug.Assert(this.RequestDescription.IsSingleResult, "this.RequestDescription.SingleResult");
            Debug.Assert(this.RequestDescription.TargetKind == RequestTargetKind.Resource, "this.RequestDescription.TargetKind == RequestTargetKind.Resource");
            Debug.Assert(element != null, "element != null");

            this.dataServicesODataWriter = this.CreateODataWriter(false /*forFeed*/);

            ResourceType targetResourceType;

            if (this.RequestDescription.TargetSource == RequestTargetSource.EntitySet ||
                this.RequestDescription.TargetSource == RequestTargetSource.ServiceOperation)
            {
                targetResourceType = this.RequestDescription.TargetResourceType;
            }
            else
            {
                Debug.Assert(
                    this.RequestDescription.TargetSource == RequestTargetSource.Property,
                    "TargetSource(" + this.RequestDescription.TargetSource + ") == Property -- otherwise control shouldn't be here.");
                Debug.Assert(this.RequestDescription.Property != null, "this.RequestDescription.Property - otherwise Property source set with no Property specified.");
                Debug.Assert(
                    this.RequestDescription.Property.TypeKind == ResourceTypeKind.EntityType,
                    "SyndicationSerializer.WriteTopLevelElement should only be called for serializing out entity types");

                targetResourceType = this.RequestDescription.Property.ResourceType;
            }

            bool needPop = this.PushSegmentForRoot();

            this.WriteEntry(
                expanded,                               // expanded
                element,                                // element
                false,                                  // resourceInstanceInFeed
                targetResourceType);                    // expectedType

            this.PopSegmentName(needPop);
        }
        /// <summary>Writes a single declared property 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 property to write out.</param>
        /// <param name="property">The resource property to write out.</param>
        /// <param name="parentUri">Uri of the object whose properties are getting written.</param>
        private void WriteObjectDeclaredProperty(IExpandedResult expanded, object customObject, ResourceProperty property, Uri parentUri)
        {
            string propertyName = property.Name;
            this.writer.WriteName(propertyName);

            // For any navigation property, we just stick the deferred element with the uri
            // This uri is different from the canonical uri: we just append the property name
            // to the parent uri. We don't want to analyze the nav property value in either case
            bool mayDefer = property.TypeKind == ResourceTypeKind.EntityType;
            if (mayDefer && !this.ShouldExpandSegment(propertyName))
            {
                this.WriteDeferredContentElement(Serializer.AppendEntryToUri(parentUri, propertyName));
            }
            else
            {
                object propertyValue;
                IExpandedResult expandedValue = null;
                if (mayDefer)
                {
                    propertyValue = GetExpandedProperty(this.Provider, expanded, customObject, property);
                    expandedValue = propertyValue as IExpandedResult;
                    if (expandedValue != null)
                    {
                        propertyValue = GetExpandedElement(expandedValue);
                    }
                }
                else
                {
                    propertyValue = GetExpandedProperty(this.Provider, null, customObject, property);
                }

                if (propertyValue == null || propertyValue == DBNull.Value)
                {
                    propertyValue = null;
                }

                bool needPop = this.PushSegmentForProperty(property);
                this.WriteElementWithName(expandedValue, propertyValue, propertyName, Serializer.AppendEntryToUri(parentUri, propertyName), false /*topLevel*/);
                this.PopSegmentName(needPop);
            }
        }
        /// <summary>
        /// Obtains the URI for the link for next page in string format
        /// </summary>
        /// <param name="lastObject">Last object serialized to be used for generating $skiptoken</param>
        /// <param name="skipTokenExpandedResult">The <see cref="IExpandedResult"/> of the $skiptoken property of object corresponding to last serialized object</param>
        /// <param name="absoluteUri">Absolute response URI</param>
        /// <returns>URI for the link for next page</returns>
        protected String GetNextLinkUri(object lastObject, IExpandedResult skipTokenExpandedResult, Uri absoluteUri)
        {
            UriBuilder builder = new UriBuilder(absoluteUri);
            SkipTokenBuilder skipTokenBuilder = null;

            if (this.IsRootContainer)
            {
                if (!this.IsCustomPaged)
                {
                    if (skipTokenExpandedResult != null)
                    {
                        skipTokenBuilder = new SkipTokenBuilderFromExpandedResult(skipTokenExpandedResult, this.RequestDescription.SkipTokenExpressionCount);
                    }       
                    else
                    {
                        Debug.Assert(this.RequestDescription.SkipTokenProperties != null, "Must have skip token properties collection");
                        Debug.Assert(this.RequestDescription.SkipTokenProperties.Count > 0, "Must have some valid ordered properties in the skip token properties collection");
                        skipTokenBuilder = new SkipTokenBuilderFromProperties(lastObject, this.Provider, this.RequestDescription.SkipTokenProperties);
                    }
                }
                else
                {
                    Debug.Assert(this.currentSkipTokenForCustomPaging != null, "Must have obtained the skip token for custom paging.");
                    skipTokenBuilder = new SkipTokenBuilderFromCustomPaging(this.currentSkipTokenForCustomPaging);
                }

                builder.Query = this.GetNextPageQueryParametersForRootContainer().Append(skipTokenBuilder.GetSkipToken()).ToString();
            }
            else
            {
                if (!this.IsCustomPaged)
                {
                    // Internal results
                    skipTokenBuilder = new SkipTokenBuilderFromProperties(lastObject, this.Provider, this.CurrentContainer.ResourceType.KeyProperties);
                }
                else
                {
                    Debug.Assert(this.currentSkipTokenForCustomPaging != null, "Must have obtained the skip token for custom paging.");
                    skipTokenBuilder = new SkipTokenBuilderFromCustomPaging(this.currentSkipTokenForCustomPaging);
                }

                builder.Query = this.GetNextPageQueryParametersForExpandedContainer().Append(skipTokenBuilder.GetSkipToken()).ToString();
            }
            
            return builder.Uri.AbsoluteUri;
        }
        /// <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="expectedType">expected type of the entry element</param>
        /// <param name="absoluteUri">absolute uri for the entry element</param>
        /// <param name="relativeUri">relative uri for the entry element</param>
        /// <param name="target">Target to write to.</param>
        private void WriteEntryElement(IExpandedResult expanded, object element, ResourceType expectedType, Uri absoluteUri, string relativeUri, SyndicationItem target)
        {
            Debug.Assert(element != null || (absoluteUri != null && !String.IsNullOrEmpty(relativeUri)), "Uri's must be specified for null values");
            Debug.Assert(target != null, "target != null");

            this.IncrementSegmentResultCount();

            string title, fullName;
            if (expectedType == null)
            {
                // If the request uri is targetting some open type properties, then we don't know the type of the resource
                // Hence we assume it to be of object type. The reason we do this is that if the value is null, there is
                // no way to know what the type of the property would be, and then we write it out as object. If the value
                // is not null, then we do get the resource type from the instance and write out the actual resource type.
                title = typeof(object).Name;
                fullName = typeof(object).FullName;
            }
            else
            {
                title = expectedType.Name;
                fullName = expectedType.FullName;
            }

            target.Title = new TextSyndicationContent(String.Empty);
            if (element == null)
            {
                SetEntryTypeName(target, fullName);
                target.AttributeExtensions[QualifiedNullAttribute] = XmlConstants.XmlTrueLiteral;
                this.WriteOtherElements(
                    element,
                    expectedType,
                    title,
                    absoluteUri,
                    relativeUri,
                    null,
                    target);

                // Don't know when we hit this code path, keeping existing behaviour in this case
                target.Authors.Add(EmptyPerson);
            }
            else
            {
                absoluteUri = Serializer.GetUri(element, this.Provider, this.CurrentContainer, this.AbsoluteServiceUri);
                Debug.Assert(absoluteUri.AbsoluteUri.StartsWith(this.AbsoluteServiceUri.AbsoluteUri, StringComparison.Ordinal), "absoluteUri.AbsoluteUri.StartsWith(this.AbsoluteServiceUri.AbsoluteUri, StringComparison.Ordinal))");
                relativeUri = absoluteUri.AbsoluteUri.Substring(this.AbsoluteServiceUri.AbsoluteUri.Length);
                ResourceType actualResourceType = WebUtil.GetNonPrimitiveResourceType(this.Provider, element);

                string mediaETag = null;
                Uri readStreamUri = null;
                string mediaContentType = null;
                if (actualResourceType.IsMediaLinkEntry)
                {
                    this.Service.StreamProvider.GetStreamDescription(element, this.Service.OperationContext, relativeUri, out mediaETag, out readStreamUri, out mediaContentType);
                }

                SetEntryTypeName(target, actualResourceType.FullName);
                this.WriteOtherElements(
                    element,
                    actualResourceType,
                    title,
                    absoluteUri,
                    relativeUri,
                    mediaETag,
                    target);

                // Write the etag property, if the type has etag properties
                string etag = this.GetETagValue(element);
                if (etag != null)
                {
                    target.AttributeExtensions[new XmlQualifiedName(XmlConstants.AtomETagAttributeName, XmlConstants.DataWebMetadataNamespace)]
                        = etag;
                }

                DictionaryContent content = new DictionaryContent(actualResourceType.Properties.Count);

                using (EpmContentSerializer epmSerializer = new EpmContentSerializer(actualResourceType, element, target, this.Provider))
                {
                    this.WriteObjectProperties(expanded, element, actualResourceType, absoluteUri, relativeUri, target, content, actualResourceType.HasEntityPropertyMappings ? actualResourceType.EpmSourceTree.Root : null);
                    epmSerializer.Serialize(content, this.Provider);
                }

                if (actualResourceType.IsMediaLinkEntry)
                {
                    // Write <content type="..." src="..." />
                    Debug.Assert(readStreamUri != null, "readStreamUri != null");
                    Debug.Assert(!string.IsNullOrEmpty(mediaContentType), "!string.IsNullOrEmpty(mediaContentType)");
                    target.Content = new UrlSyndicationContent(readStreamUri, mediaContentType);
                    if (!content.IsEmpty)
                    {
                        // Since UrlSyndicationContent must have empty content, we write the <m:property /> node as SyndicationElementExtension.
                        target.ElementExtensions.Add(content.GetPropertyContentsReader());
                    }
                }
                else
                {
                    target.Content = content;
                }
            }

#if ASTORIA_FF_CALLBACKS
            this.Service.InternalOnWriteItem(target, element);
#endif                
        }
Beispiel #30
0
        /// <summary>Writes a single top-level element.</summary>
        /// <param name="expanded">Expanded properties for the result.</param>
        /// <param name="element">Element to write, possibly null.</param>
        protected override void WriteTopLevelElement(IExpandedResult expanded, object element)
        {
            Debug.Assert(this.RequestDescription.IsSingleResult, "this.RequestDescription.SingleResult");
            Debug.Assert(this.RequestDescription.TargetKind == RequestTargetKind.Resource, "this.RequestDescription.TargetKind == RequestTargetKind.Resource");
            Debug.Assert(element != null, "element != null");

            this.dataServicesODataWriter = this.CreateODataWriter(false /*forFeed*/);

            ResourceType targetResourceType;
            if (this.RequestDescription.TargetSource == RequestTargetSource.EntitySet ||
                this.RequestDescription.TargetSource == RequestTargetSource.ServiceOperation)
            {
                targetResourceType = this.RequestDescription.TargetResourceType;
            }
            else
            {
                Debug.Assert(
                    this.RequestDescription.TargetSource == RequestTargetSource.Property,
                    "TargetSource(" + this.RequestDescription.TargetSource + ") == Property -- otherwise control shouldn't be here.");
                Debug.Assert(this.RequestDescription.Property != null, "this.RequestDescription.Property - otherwise Property source set with no Property specified.");
                Debug.Assert(
                    this.RequestDescription.Property.TypeKind == ResourceTypeKind.EntityType,
                    "SyndicationSerializer.WriteTopLevelElement should only be called for serializing out entity types");

                targetResourceType = this.RequestDescription.Property.ResourceType;
            }

            bool needPop = this.PushSegmentForRoot();
            this.WriteEntry(
                expanded,                               // expanded
                element,                                // element
                false,                                  // resourceInstanceInFeed
                targetResourceType);                    // expectedType

            this.PopSegmentName(needPop);
        }
Beispiel #31
0
        /// <summary>Writes multiple top-level elements, possibly none.</summary>
        /// <param name="expanded">Expanded properties for the result.</param>
        /// <param name="elements">Enumerator for elements to write.</param>
        protected override void WriteTopLevelElements(IExpandedResult expanded, QueryResultInfo elements)
        {
            Debug.Assert(elements != null, "elements != null");
            Debug.Assert(!this.RequestDescription.IsSingleResult, "!this.RequestDescription.SingleResult");

            string title;
            if (this.RequestDescription.TargetKind != RequestTargetKind.OpenProperty &&
                this.RequestDescription.TargetSource == RequestTargetSource.Property)
            {
                title = this.RequestDescription.Property.Name;
            }
            else
            {
                title = this.RequestDescription.ContainerName;
            }

            this.dataServicesODataWriter = this.CreateODataWriter(true /*forFeed*/);

            bool needPop = this.PushSegmentForRoot();
            this.WriteFeedElements(
                expanded,
                elements,
                this.RequestDescription.TargetResourceType.ElementType(),
                title,                                      // title
                () => new Uri(this.RequestDescription.LastSegmentInfo.Identifier, UriKind.Relative),
                () => this.RequestDescription.ResultUri,    // absoluteUri
                true);

            this.PopSegmentName(needPop);
        }
Beispiel #32
0
        /// <summary>
        /// Writes the feed element for the atom payload.
        /// </summary>
        /// <param name="expanded">Expanded properties for the result.</param>
        /// <param name="elements">Collection of entries in the feed element.</param>
        /// <param name="expectedType">ExpectedType of the elements in the collection.</param>
        /// <param name="title">Title of the feed element.</param>
        /// <param name="getRelativeUri">Callback to get the relative uri of the feed.</param>
        /// <param name="getAbsoluteUri">Callback to get the absolute uri of the feed.</param>
        /// <param name="topLevel">True if the feed is the top level feed, otherwise false for the inner expanded feed.</param>
        private void WriteFeedElements(
            IExpandedResult expanded,
            QueryResultInfo elements,
            ResourceType expectedType,
            string title,
            Func<Uri> getRelativeUri,
            Func<Uri> getAbsoluteUri,
            bool topLevel)
        {
            Debug.Assert(elements != null, "elements != null");
            Debug.Assert(expectedType != null && expectedType.ResourceTypeKind == ResourceTypeKind.EntityType, "expectedType != null && expectedType.ResourceTypeKind == ResourceTypeKind.EntityType");
            Debug.Assert(!string.IsNullOrEmpty(title), "!string.IsNullOrEmpty(title)");

            ODataFeed feed = new ODataFeed();
            feed.SetSerializationInfo(new ODataFeedAndEntrySerializationInfo { NavigationSourceName = this.CurrentContainer.Name, NavigationSourceEntityTypeName = this.CurrentContainer.ResourceType.FullName, ExpectedTypeName = expectedType.FullName });

            // Write the other elements for the feed
            this.PayloadMetadataPropertyManager.SetId(feed, () => getAbsoluteUri());

#pragma warning disable 618
            if (this.contentFormat == ODataFormat.Atom)
#pragma warning restore 618
            {
                // Create the atom feed metadata and set the self link and title value.
                AtomFeedMetadata feedMetadata = new AtomFeedMetadata();
                feed.SetAnnotation(feedMetadata);
                feedMetadata.Title = new AtomTextConstruct { Text = title };
                feedMetadata.SelfLink = new AtomLinkMetadata { Href = getRelativeUri(), Title = title };
            }

            // support for $count
            // in ATOM we write it at the beginning (we always have)
            //   in JSON for backward compatiblity reasons we write it at the end, so we must not fill it here.
            if (topLevel && this.RequestDescription.CountOption == RequestQueryCountOption.CountQuery)
            {
                feed.Count = this.RequestDescription.CountValue;
            }

            var feedArgs = elements.GetDataServiceODataWriterFeedArgs(feed, this.Service.OperationContext);
            this.dataServicesODataWriter.WriteStart(feedArgs);
            object lastObject = null;
            IExpandedResult lastExpandedSkipToken = null;
            while (elements.HasMoved)
            {
                object o = elements.Current;
                IExpandedResult skipToken = this.GetSkipToken(expanded);

                if (o != null)
                {
                    IExpandedResult expandedO = o as IExpandedResult;
                    if (expandedO != null)
                    {
                        expanded = expandedO;
                        o = GetExpandedElement(expanded);
                        skipToken = this.GetSkipToken(expanded);
                    }

                    this.WriteEntry(expanded, o, true /*resourceInstanceInFeed*/, expectedType);
                }

                elements.MoveNext();
                lastObject = o;
                lastExpandedSkipToken = skipToken;
            }

            // After looping through the objects in the sequence, decide if we need to write the next
            // page link and if yes, write it by invoking the delegate
            if (this.NeedNextPageLink(elements))
            {
                this.PayloadMetadataPropertyManager.SetNextPageLink(
                    feed,
                    this.AbsoluteServiceUri,
                    this.GetNextLinkUri(lastObject, lastExpandedSkipToken, getAbsoluteUri()));
            }

            this.dataServicesODataWriter.WriteEnd(feedArgs);
#if ASTORIA_FF_CALLBACKS
            this.Service.InternalOnWriteFeed(feed);
#endif
        }
 /// <summary>Writes multiple top-level elements, possibly none.</summary>
 /// <param name="expanded">Expanded properties for the result.</param>
 /// <param name="elements">Result elements.</param>
 /// <param name="hasMoved">Whether <paramref name="elements"/> was succesfully advanced to the first element.</param>
 protected abstract void WriteTopLevelElements(IExpandedResult expanded, IEnumerator elements, bool hasMoved);
        /// <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>Writes a single top-level element.</summary>
        /// <param name="expanded">Expanded properties for the result.</param>
        /// <param name="element">Element to write, possibly null.</param>
        protected override void WriteTopLevelElement(IExpandedResult expanded, object element)
        {
            Debug.Assert(this.RequestDescription.IsSingleResult, "this.RequestDescription.SingleResult");
            Debug.Assert(element != null, "element != null");

            this.resultItem = new SyndicationItem();
            this.resultItem.BaseUri = this.AbsoluteServiceUri;
            IncludeCommonNamespaces(this.resultItem.AttributeExtensions);

            if (this.RequestDescription.TargetSource == RequestTargetSource.EntitySet ||
                this.RequestDescription.TargetSource == RequestTargetSource.ServiceOperation)
            {
                bool needPop = this.PushSegmentForRoot();
                this.WriteEntryElement(
                    expanded,
                    element,
                    this.RequestDescription.TargetResourceType,
                    this.RequestDescription.ResultUri,
                    this.RequestDescription.ContainerName,
                    this.resultItem);
                this.PopSegmentName(needPop);
            }
            else
            {
                Debug.Assert(
                    this.RequestDescription.TargetSource == RequestTargetSource.Property,
                    "TargetSource(" + this.RequestDescription.TargetSource + ") == Property -- otherwise control shouldn't be here.");
                ResourceType resourcePropertyType;
                if (this.RequestDescription.TargetKind == RequestTargetKind.OpenProperty)
                {
                    resourcePropertyType = (element == null) ? ResourceType.PrimitiveStringResourceType : WebUtil.GetResourceType(this.Provider, element);
                    if (resourcePropertyType == null)
                    {
                        Type propertyType = element == null ? typeof(string) : element.GetType();
                        throw new InvalidOperationException(Strings.Serializer_UnsupportedTopLevelType(propertyType));
                    }
                }
                else
                {
                    Debug.Assert(this.RequestDescription.Property != null, "this.RequestDescription.Property - otherwise Property source set with no Property specified.");
                    ResourceProperty property = this.RequestDescription.Property;
                    resourcePropertyType = property.ResourceType;
                }

                Debug.Assert(
                    resourcePropertyType.ResourceTypeKind == ResourceTypeKind.EntityType,
                    "Open ResourceTypeKind == EnityType -- temporarily, because ATOM is the only implemented syndication serializer and doesn't support it.");

                bool needPop = this.PushSegmentForRoot();
                this.WriteEntryElement(
                    expanded,                               // expanded
                    element,                                // element
                    resourcePropertyType,              // expectedType
                    this.RequestDescription.ResultUri,      // absoluteUri
                    this.RequestDescription.ContainerName,  // relativeUri
                    this.resultItem);                       // target
                this.PopSegmentName(needPop);
            }

            // Since the element is not equal to null, the factory should never return null
            SyndicationItemFormatter formatter = this.factory.CreateSyndicationItemFormatter(this.resultItem);
            formatter.WriteTo(this.writer);
        }
        /// <summary>Provides an enumeration of deferred feed items.</summary>
        /// <param name="expanded">Expanded properties for the result.</param>
        /// <param name="elements">Elements to enumerate.</param>
        /// <param name="expectedType">Expected type of elements.</param>
        /// <param name="hasMoved">Whether the enumerator moved to the first element.</param>
        /// <param name="activeSegmentNames">The segment names active at this point in serialization.</param>
        /// <param name="nextPageLinkWriter">Delegate that writes the next page link if necessity arises</param>
        /// <param name="disposeElements">If set to true the function should dispose the elements enumerator (always).</param>
        /// <returns>An object that can enumerate syndication items.</returns>
        private IEnumerable<SyndicationItem> DeferredFeedItems(
            IExpandedResult expanded, 
            IEnumerator elements,
            ResourceType expectedType, 
            bool hasMoved, 
            object activeSegmentNames,
            Action<object, IExpandedResult> nextPageLinkWriter,
            bool disposeElements)
        {
            try
            {
                object savedSegmentNames = this.SaveSegmentNames();
                this.RestoreSegmentNames(activeSegmentNames);
                object lastObject = null;
                IExpandedResult lastExpandedSkipToken = null;
                while (hasMoved)
                {
                    object o = elements.Current;
                    IExpandedResult skipToken = this.GetSkipToken(expanded);
                    
                    if (o != null)
                    {
                        SyndicationItem target = new SyndicationItem();
                        IExpandedResult expandedO = o as IExpandedResult;
                        if (expandedO != null)
                        {
                            expanded = expandedO;
                            o = GetExpandedElement(expanded);
                            skipToken = this.GetSkipToken(expanded);
                        }

                        this.WriteEntryElement(expanded, o, expectedType, null, null, target);
                        yield return target;
                    }

                    hasMoved = elements.MoveNext();
                    lastObject = o;
                    lastExpandedSkipToken = skipToken;
                }

                // After looping through the objects in the sequence, decide if we need to write the next
                // page link and if yes, write it by invoking the delegate
                if (this.NeedNextPageLink(elements))
                {
                    nextPageLinkWriter(lastObject, lastExpandedSkipToken);
                }

                this.RestoreSegmentNames(savedSegmentNames);
            }
            finally
            {
                if (disposeElements)
                {
                    WebUtil.Dispose(elements);
                }
            }
        }
 /// <summary>
 /// Writes the next page link to the current xml writer corresponding to the feed
 /// </summary>
 /// <param name="lastElement">Object that will contain the keys for skip token</param>
 /// <param name="expandedResult">The <see cref="IExpandedResult"/> of the $skiptoken property of the object being written</param>
 /// <param name="absoluteUri">Absolute URI for the result</param>
 private void WriteNextPageLink(object lastElement, IExpandedResult expandedResult, Uri absoluteUri)
 {
     this.writer.WriteStartElement("link", XmlConstants.AtomNamespace);
     this.writer.WriteAttributeString("rel", "next");
     this.writer.WriteAttributeString("href", this.GetNextLinkUri(lastElement, expandedResult, absoluteUri));
     this.writer.WriteEndElement();
 }
        /// <summary>
        /// Writes the feed element for the atom payload
        /// </summary>
        /// <param name="expanded">Expanded properties for the result.</param>
        /// <param name="elements">collection of entries in the feed element</param>
        /// <param name="expectedType">expectedType of the elements in the collection</param>
        /// <param name="title">title of the feed element</param>
        /// <param name="absoluteUri">absolute uri representing the feed element</param>
        /// <param name="relativeUri">relative uri representing the feed element</param>
        /// <param name="hasMoved">whether the enumerator has successfully moved to the first element</param>
        /// <param name='feed'>Feed to write to.</param>
        /// <param name="disposeElementsOnSuccess">If set to true the function should dispose the elements enumerator when it's done
        /// with it. Not in the case this method fails though.</param>
        private void WriteFeedElements(
            IExpandedResult expanded, 
            IEnumerator elements, 
            ResourceType expectedType, 
            string title, 
            Uri absoluteUri, 
            string relativeUri, 
            bool hasMoved, 
            SyndicationFeed feed, 
            bool disposeElementsOnSuccess)
        {
            Debug.Assert(feed != null, "feed != null");

            // Write the other elements for the feed
            feed.Id = absoluteUri.AbsoluteUri;
            feed.Title = new TextSyndicationContent(title);
            var uri = new Uri(relativeUri, UriKind.Relative);
            var link = new SyndicationLink(uri, XmlConstants.AtomSelfRelationAttributeValue, title, null, 0L);
            feed.Links.Add(link);

            if (!hasMoved)
            {
                // ATOM specification: if a feed contains no entries, then the feed should have at least one Author tag
                feed.Authors.Add(EmptyPerson);
            }
            
            // Instead of looping, create an item that will defer the production of SyndicationItem instances.
            // PERF: consider optimizing out empty collections when hasMoved is false.
            feed.Items = this.DeferredFeedItems(
                expanded, 
                elements, 
                expectedType, 
                hasMoved, 
                this.SaveSegmentNames(), 
                (o, e) => this.WriteNextPageLink(o, e, absoluteUri),
                disposeElementsOnSuccess);
#if ASTORIA_FF_CALLBACKS
            this.Service.InternalOnWriteFeed(feed);
#endif            
        }
        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);
                    }
                }
            }
        }
Beispiel #40
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
            }
        }
        /// <summary>Writes a single top-level element.</summary>
        /// <param name="expanded">Expanded properties for the result.</param>
        /// <param name="element">Element to write, possibly null.</param>
        protected override void WriteTopLevelElement(IExpandedResult expanded, object element)
        {
            Debug.Assert(this.RequestDescription.IsSingleResult, "this.RequestDescription.SingleResult");

            bool needPop = this.PushSegmentForRoot();
            this.writer.StartObjectScope(); // {
            this.writer.WriteDataWrapper(); // "d" :
            this.WriteElementWithName(expanded, element, this.RequestDescription.ContainerName, this.RequestDescription.ResultUri, true /*topLevel*/);
            this.writer.EndScope();         // }
            this.PopSegmentName(needPop);
        }
Beispiel #42
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
            }
        }
Beispiel #43
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
        }
Beispiel #44
0
 protected override void WriteTopLevelElements(IExpandedResult expanded, IEnumerator elements, bool hasMoved)
 {
     if (base.RequestDescription.LinkUri)
     {
         bool needPop = base.PushSegmentForRoot();
         this.WriteLinkCollection(elements, hasMoved);
         base.PopSegmentName(needPop);
     }
     else
     {
         this.collectionWriter = this.writer.CreateODataCollectionWriter();
         ODataCollectionStart collectionStart = new ODataCollectionStart {
             Name = this.ComputeContainerName()
         };
         this.collectionWriter.WriteStart(collectionStart);
         while (hasMoved)
         {
             object current = elements.Current;
             ResourceType propertyResourceType = (current == null) ? base.RequestDescription.TargetResourceType : WebUtil.GetResourceType(base.Provider, current);
             if (propertyResourceType == null)
             {
                 throw new InvalidOperationException(System.Data.Services.Strings.Serializer_UnsupportedTopLevelType(current.GetType()));
             }
             this.collectionWriter.WriteItem(base.GetPropertyValue("element", propertyResourceType, current, false));
             hasMoved = elements.MoveNext();
         }
         this.collectionWriter.WriteEnd();
         this.collectionWriter.Flush();
     }
 }
 /// <summary>Gets the expandable value for the specified object.</summary>
 /// <param name="provider">underlying data source instance.</param>
 /// <param name="expanded">Expanded properties for the result, possibly null.</param>
 /// <param name="customObject">Object with value to retrieve.</param>
 /// <param name="property">Property for which value will be retrieved.</param>
 /// <returns>The property value.</returns>
 protected static object GetExpandedProperty(DataServiceProviderWrapper provider, IExpandedResult expanded, object customObject, ResourceProperty property)
 {
     Debug.Assert(property != null, "property != null");
     if (expanded == null)
     {
         return WebUtil.GetPropertyValue(provider, customObject, null, property, null);
     }
     else
     {
         // We may end up projecting null as a value of ResourceSetReference property. This can in theory break
         //   the serializers as they expect a non-null (possibly empty) IEnumerable instead. But note that
         //   if we project null into the expanded property, we also project null into the ExpandedElement property
         //   and thus the serializers should recognize this value as null and don't try to expand its properties.
         Debug.Assert(
             expanded.ExpandedElement != null, 
             "We should not be accessing expanded properties on null resource.");
         return expanded.GetExpandedPropertyValue(property.Name);
     }
 }
Beispiel #46
0
        private void WriteElementWithName(IExpandedResult expanded, object element, string elementName, Uri elementUri, bool topLevel)
        {
            Debug.Assert(elementName == null || elementName.Length > 0, "elementName == null || elementName.Length > 0");
            Debug.Assert(elementUri != null, "elementUri != null");

            this.RecurseEnter();
            try
            {
                if (element == null)
                {
                    if (topLevel)
                    {
                        this.writer.StartObjectScope();
                        this.writer.WriteName(elementName);
                        this.WriteNullValue();
                        this.writer.EndScope();
                    }
                    else
                    {
                        this.WriteNullValue();
                    }
                }
                else
                {
                    IEnumerable enumerableElement;
                    if (WebUtil.IsElementIEnumerable(element, out enumerableElement))
                    {
                        if (this.JsonFormatVersion >= 2)
                        {
                            // JSON V2 Nested Collections:
                            // {
                            //      "results": []
                            //      "__next" : uri
                            // }
                            this.writer.StartObjectScope();
                            this.writer.WriteDataArrayName();
                        }

                        this.writer.StartArrayScope();
                        IEnumerator elements = enumerableElement.GetEnumerator();

                        try
                        {
                            IExpandedResult expandedEnumerator    = elements as IExpandedResult;
                            object          lastObject            = null;
                            IExpandedResult lastExpandedSkipToken = null;

                            while (elements.MoveNext())
                            {
                                object          elementInCollection = elements.Current;
                                IExpandedResult skipToken           = this.GetSkipToken(expandedEnumerator);
                                if (elementInCollection != null)
                                {
                                    IExpandedResult expandedElementInCollection = elementInCollection as IExpandedResult;
                                    if (expandedElementInCollection != null)
                                    {
                                        expandedEnumerator  = expandedElementInCollection;
                                        elementInCollection = GetExpandedElement(expandedEnumerator);
                                        skipToken           = this.GetSkipToken(expandedEnumerator);
                                    }

                                    this.WriteElementWithName(expandedEnumerator, elementInCollection, null, elementUri, false /*topLevel*/);
                                }

                                lastObject            = elementInCollection;
                                lastExpandedSkipToken = skipToken;
                            }

                            this.writer.EndScope(); // array scope

                            if (this.JsonFormatVersion >= 2)
                            {
                                // After looping through the objects in the sequence, decide if we need to write the next
                                // page link and if yes, write it by invoking the delegate
                                if (this.NeedNextPageLink(elements))
                                {
                                    this.WriteNextPageLink(lastObject, lastExpandedSkipToken, elementUri);
                                }

                                this.writer.EndScope(); // expanded object scope
                            }
                        }
                        finally
                        {
                            WebUtil.Dispose(elements);
                        }

                        return;
                    }

                    ResourceType resourceType = WebUtil.GetResourceType(this.Provider, element);
                    if (resourceType == null)
                    {
                        // Skip this element.
                        return;
                    }

                    switch (resourceType.ResourceTypeKind)
                    {
                    case ResourceTypeKind.ComplexType:
                        if (topLevel)
                        {
                            this.writer.StartObjectScope();
                            this.writer.WriteName(elementName);
                        }

                        // Non-value complex types may form a cycle.
                        // PERF: we can keep a single element around and save the HashSet initialization
                        // until we find a second complex type - this saves the allocation on trees
                        // with shallow (single-level) complex types.
                        if (this.AddToComplexTypeCollection(element))
                        {
                            this.WriteComplexTypeProperties(element, resourceType, elementUri);
                            this.RemoveFromComplexTypeCollection(element);
                        }
                        else
                        {
                            throw new InvalidOperationException(Strings.Serializer_LoopsNotAllowedInComplexTypes(elementName));
                        }

                        if (topLevel)
                        {
                            this.writer.EndScope();
                        }

                        break;

                    case ResourceTypeKind.EntityType:
                        this.IncrementSegmentResultCount();
                        this.writer.StartObjectScope();
                        Uri entityUri = Serializer.GetUri(element, this.Provider, this.CurrentContainer, this.AbsoluteServiceUri);
                        this.WriteMetadataObject(element, resourceType, entityUri);
                        this.WriteResourceProperties(expanded, element, resourceType, entityUri);
                        this.writer.EndScope();
                        break;

                    default:
                        Debug.Assert(resourceType.ResourceTypeKind == ResourceTypeKind.Primitive, "resourceType.ResourceTypeKind == ResourceTypeKind.Primitive");
                        if (topLevel)
                        {
                            this.writer.StartObjectScope();
                            this.writer.WriteName(elementName);
                            this.WritePrimitiveValue(element);
                            this.writer.EndScope();
                        }
                        else
                        {
                            this.WritePrimitiveValue(element);
                        }

                        break;
                    }
                }
            }
            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>Constructor.</summary>
 /// <param name="skipTokenExpandedResult">IExpandedResult to lookup for skip token values.</param>
 /// <param name="skipTokenExpressionCount">Number of values in skip token.</param>
 public SkipTokenBuilderFromExpandedResult(IExpandedResult skipTokenExpandedResult, int skipTokenExpressionCount)
     : base()
 {
     this.skipTokenExpandedResult = skipTokenExpandedResult;
     this.skipTokenExpressionCount = skipTokenExpressionCount;
 }
Beispiel #48
0
 protected override void WriteTopLevelElement(IExpandedResult expandedResult, object element)
 {
     string propertyName = this.ComputeContainerName();
     if (base.RequestDescription.LinkUri)
     {
         bool needPop = base.PushSegmentForRoot();
         this.WriteLink(element);
         base.PopSegmentName(needPop);
     }
     else
     {
         ResourceType type;
         if (element == null)
         {
             type = (base.RequestDescription.TargetKind == RequestTargetKind.OpenProperty) ? ResourceType.PrimitiveStringResourceType : base.RequestDescription.TargetResourceType;
         }
         else
         {
             type = (base.RequestDescription.TargetKind == RequestTargetKind.Collection) ? base.RequestDescription.TargetResourceType : WebUtil.GetResourceType(base.Provider, element);
         }
         if (type == null)
         {
             throw new InvalidOperationException(System.Data.Services.Strings.Serializer_UnsupportedTopLevelType(element.GetType()));
         }
         ODataProperty property = new ODataProperty {
             Name = propertyName,
             Value = base.GetPropertyValue(propertyName, type, element, false)
         };
         this.writer.WriteProperty(property);
     }
 }
 /// <summary>Writes a single top-level element.</summary>
 /// <param name="expanded">Expanded properties for the result.</param>
 /// <param name="element">Element to write, possibly null.</param>
 protected abstract void WriteTopLevelElement(IExpandedResult expanded, object element);
        /// <summary>Writes multiple top-level elements, possibly none.</summary>
        /// <param name="expanded">Expanded properties for the result.</param>
        /// <param name="elements">Enumerator for elements to write.</param>
        /// <param name="hasMoved">Whether <paramref name="elements"/> was succesfully advanced to the first element.</param>
        protected override void WriteTopLevelElements(IExpandedResult expanded, IEnumerator elements, bool hasMoved)
        {
            Debug.Assert(elements != null, "elements != null");
            Debug.Assert(!this.RequestDescription.IsSingleResult, "!this.RequestDescription.SingleResult");

            string title;
            if (this.RequestDescription.TargetKind != RequestTargetKind.OpenProperty &&
                this.RequestDescription.TargetSource == RequestTargetSource.Property)
            {
                title = this.RequestDescription.Property.Name;
            }
            else
            {
                title = this.RequestDescription.ContainerName;
            }

            this.resultFeed = new SyndicationFeed();
            IncludeCommonNamespaces(this.resultFeed.AttributeExtensions);
            this.resultFeed.BaseUri = RequestUriProcessor.AppendEscapedSegment(this.AbsoluteServiceUri, "");
            string relativeUri = this.RequestDescription.LastSegmentInfo.Identifier;

            // support for $count
            if (this.RequestDescription.CountOption == RequestQueryCountOption.Inline)
            {
                this.WriteRowCount();
            }
            
            bool needPop = this.PushSegmentForRoot();

            this.WriteFeedElements(
                expanded,
                elements,
                this.RequestDescription.TargetResourceType,
                title,                                      // title
                this.RequestDescription.ResultUri,          // absoluteUri
                relativeUri,                                // relativeUri 
                hasMoved,                                   // hasMoved
                this.resultFeed,                            // feed
                false);

            this.PopSegmentName(needPop);

            SyndicationFeedFormatter formatter = this.factory.CreateSyndicationFeedFormatter(this.resultFeed);
            formatter.WriteTo(this.writer);
        }
Beispiel #51
0
        /// <summary>Writes multiple top-level elements, possibly none.</summary>
        /// <param name="expanded">Expanded properties for the result.</param>
        /// <param name="elements">Enumerator for elements to write.</param>
        /// <param name="hasMoved">Whether <paramref name="elements"/> was succesfully advanced to the first element.</param>
        protected override void WriteTopLevelElements(IExpandedResult expanded, IEnumerator elements, bool hasMoved)
        {
            Debug.Assert(elements != null, "elements != null");
            Debug.Assert(!this.RequestDescription.IsSingleResult, "!this.RequestDescription.SingleResult");

            bool needPop   = this.PushSegmentForRoot();
            Uri  parentUri = this.RequestDescription.ResultUri;

            this.writer.StartObjectScope();
            this.writer.WriteDataWrapper();

            if (this.JsonFormatVersion >= 2)
            {
                // Json Format V2:
                // { "d" :
                //      {
                //          "__results": [],
                //          "__count": 0,
                //          "__next" : uri
                //      }
                // }
                this.writer.StartObjectScope();
                this.writer.WriteDataArrayName();
            }

            // Json Format V1:
            // { "d" : [] }
            this.writer.StartArrayScope();

            object          lastObject            = null;
            IExpandedResult lastExpandedSkipToken = null;

            while (hasMoved)
            {
                object          o         = elements.Current;
                IExpandedResult skipToken = this.GetSkipToken(expanded);
                if (o != null)
                {
                    IExpandedResult expandedObject = o as IExpandedResult;
                    if (expandedObject != null)
                    {
                        expanded  = expandedObject;
                        o         = GetExpandedElement(expanded);
                        skipToken = this.GetSkipToken(expanded);
                    }

                    this.WriteElementWithName(expanded, o, null, parentUri, false /*topLevel*/);
                }

                hasMoved              = elements.MoveNext();
                lastObject            = o;
                lastExpandedSkipToken = skipToken;
            }

            this.writer.EndScope();     // end data array

            if (this.JsonFormatVersion >= 2)
            {
                // $count=inline support
                if (this.RequestDescription.CountOption == RequestQueryCountOption.Inline)
                {
                    this.WriteRowCount();
                }

                // After looping through the objects in the sequence, decide if we need to write the next
                // page link and if yes, write it by invoking the delegate
                if (this.NeedNextPageLink(elements))
                {
                    this.WriteNextPageLink(lastObject, lastExpandedSkipToken, parentUri);
                }

                this.writer.EndScope();     // end object scope
            }

            this.writer.EndScope();     // end "d" wrapper

            this.PopSegmentName(needPop);
        }
Beispiel #52
0
        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);
                    }
                }
            }
        }
Beispiel #53
0
 /// <summary>
 /// Writes the next page link to the current xml writer corresponding to the feed
 /// </summary>
 /// <param name="lastElement">Object that will contain the keys for skip token</param>
 /// <param name="skipTokenExpandedResult">The <see cref="IExpandedResult"/> of the $skiptoken property of the object being written</param>
 /// <param name="absoluteUri">Absolute URI for the result</param>
 private void WriteNextPageLink(object lastElement, IExpandedResult skipTokenExpandedResult, Uri absoluteUri)
 {
     this.writer.WriteName(XmlConstants.JsonNextString);
     this.writer.WriteValue(this.GetNextLinkUri(lastElement, skipTokenExpandedResult, absoluteUri));
 }
 /// <summary>Gets the expanded element for the specified expanded result.</summary>
 /// <param name="expanded">The expanded result to process.</param>
 /// <returns>The expanded element.</returns>
 protected static object GetExpandedElement(IExpandedResult expanded)
 {
     Debug.Assert(expanded != null, "expanded != null");
     return expanded.ExpandedElement;
 }
        private void WriteElementWithName(IExpandedResult expanded, object element, string elementName, Uri elementUri, bool topLevel)
        {
            Debug.Assert(elementName == null || elementName.Length > 0, "elementName == null || elementName.Length > 0");
            Debug.Assert(elementUri != null, "elementUri != null");

            this.RecurseEnter();
            try
            {
                if (element == null)
                {
                    if (topLevel)
                    {
                        this.writer.StartObjectScope();
                        this.writer.WriteName(elementName);
                        this.WriteNullValue();
                        this.writer.EndScope();
                    }
                    else
                    {
                        this.WriteNullValue();
                    }
                }
                else
                {
                    IEnumerable enumerableElement;
                    if (WebUtil.IsElementIEnumerable(element, out enumerableElement))
                    {
                        if (this.JsonFormatVersion >= 2)
                        {
                            // JSON V2 Nested Collections:
                            // {
                            //      "results": []
                            //      "__next" : uri
                            // }
                            this.writer.StartObjectScope();
                            this.writer.WriteDataArrayName();
                        }

                        this.writer.StartArrayScope();
                        IEnumerator elements = enumerableElement.GetEnumerator();

                        try
                        {
                            IExpandedResult expandedEnumerator = elements as IExpandedResult;
                            object lastObject = null;
                            IExpandedResult lastExpandedSkipToken = null;

                            while (elements.MoveNext())
                            {
                                object elementInCollection = elements.Current;
                                IExpandedResult skipToken = this.GetSkipToken(expandedEnumerator);
                                if (elementInCollection != null)
                                {
                                    IExpandedResult expandedElementInCollection = elementInCollection as IExpandedResult;
                                    if (expandedElementInCollection != null)
                                    {
                                        expandedEnumerator = expandedElementInCollection;
                                        elementInCollection = GetExpandedElement(expandedEnumerator);
                                        skipToken = this.GetSkipToken(expandedEnumerator);
                                    }

                                    this.WriteElementWithName(expandedEnumerator, elementInCollection, null, elementUri, false /*topLevel*/);
                                }

                                lastObject = elementInCollection;
                                lastExpandedSkipToken = skipToken;
                            }

                            this.writer.EndScope(); // array scope

                            if (this.JsonFormatVersion >= 2)
                            {
                                // After looping through the objects in the sequence, decide if we need to write the next
                                // page link and if yes, write it by invoking the delegate
                                if (this.NeedNextPageLink(elements))
                                {
                                    this.WriteNextPageLink(lastObject, lastExpandedSkipToken, elementUri);
                                }

                                this.writer.EndScope(); // expanded object scope
                            }
                        }
                        finally
                        {
                            WebUtil.Dispose(elements);
                        }

                        return;
                    }

                    ResourceType resourceType = WebUtil.GetResourceType(this.Provider, element);
                    if (resourceType == null)
                    {
                        // Skip this element.
                        return;
                    }

                    switch (resourceType.ResourceTypeKind)
                    {
                        case ResourceTypeKind.ComplexType:
                            if (topLevel)
                            {
                                this.writer.StartObjectScope();
                                this.writer.WriteName(elementName);
                            }

                            // Non-value complex types may form a cycle.
                            // PERF: we can keep a single element around and save the HashSet initialization
                            // until we find a second complex type - this saves the allocation on trees
                            // with shallow (single-level) complex types.
                            if (this.AddToComplexTypeCollection(element))
                            {
                                this.WriteComplexTypeProperties(element, resourceType, elementUri);
                                this.RemoveFromComplexTypeCollection(element);
                            }
                            else
                            {
                                throw new InvalidOperationException(Strings.Serializer_LoopsNotAllowedInComplexTypes(elementName));
                            }

                            if (topLevel)
                            {
                                this.writer.EndScope();
                            }

                            break;
                        case ResourceTypeKind.EntityType:
                            this.IncrementSegmentResultCount();
                            this.writer.StartObjectScope();
                            Uri entityUri = Serializer.GetUri(element, this.Provider, this.CurrentContainer, this.AbsoluteServiceUri);
                            this.WriteMetadataObject(element, resourceType, entityUri);
                            this.WriteResourceProperties(expanded, element, resourceType, entityUri);
                            this.writer.EndScope();
                            break;
                        default:
                            Debug.Assert(resourceType.ResourceTypeKind == ResourceTypeKind.Primitive, "resourceType.ResourceTypeKind == ResourceTypeKind.Primitive");
                            if (topLevel)
                            {
                                this.writer.StartObjectScope();
                                this.writer.WriteName(elementName);
                                this.WritePrimitiveValue(element);
                                this.writer.EndScope();
                            }
                            else
                            {
                                this.WritePrimitiveValue(element);
                            }

                            break;
                    }
                }
            }
            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>Writes all the properties of the specified resource.</summary>
        /// <param name="expanded">Expanded properties for the result.</param>
        /// <param name="resource">Resource with properties to write out.</param>
        /// <param name="resourceType">Type for the specified resource (saves the lookup in this method).</param>
        /// <param name="uri">uri of the resource whose properties are getting written</param>
        private void WriteResourceProperties(IExpandedResult expanded, object resource, ResourceType resourceType, Uri uri)
        {
            Debug.Assert(resource != null, "resource != null");
            Debug.Assert(resourceType != null, "resourceType != null");
            Debug.Assert(resourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "resource must be entity type");
            Debug.Assert(uri != null, "uri != null");

            this.WriteObjectProperties(expanded, resource, resourceType, uri, true);
        }
        /// <summary>Writes multiple top-level elements, possibly none.</summary>
        /// <param name="expanded">Expanded properties for the result.</param>
        /// <param name="elements">Enumerator for elements to write.</param>
        /// <param name="hasMoved">Whether <paramref name="elements"/> was succesfully advanced to the first element.</param>
        protected override void WriteTopLevelElements(IExpandedResult expanded, IEnumerator elements, bool hasMoved)
        {
            Debug.Assert(elements != null, "elements != null");
            Debug.Assert(!this.RequestDescription.IsSingleResult, "!this.RequestDescription.SingleResult");

            bool needPop = this.PushSegmentForRoot();
            Uri parentUri = this.RequestDescription.ResultUri;
            this.writer.StartObjectScope();
            this.writer.WriteDataWrapper();

            if (this.JsonFormatVersion >= 2)
            {
                // Json Format V2:
                // { "d" :
                //      {
                //          "__results": [],
                //          "__count": 0,
                //          "__next" : uri
                //      }
                // }
                this.writer.StartObjectScope();
                this.writer.WriteDataArrayName();
            }

            // Json Format V1:
            // { "d" : [] }
            this.writer.StartArrayScope();

            object lastObject = null;
            IExpandedResult lastExpandedSkipToken = null;
            while (hasMoved)
            {
                object o = elements.Current;
                IExpandedResult skipToken = this.GetSkipToken(expanded);
                if (o != null)
                {
                    IExpandedResult expandedObject = o as IExpandedResult;
                    if (expandedObject != null)
                    {
                        expanded = expandedObject;
                        o = GetExpandedElement(expanded);
                        skipToken = this.GetSkipToken(expanded);
                    }
                
                    this.WriteElementWithName(expanded, o, null, parentUri, false /*topLevel*/);
                }

                hasMoved = elements.MoveNext();
                lastObject = o;
                lastExpandedSkipToken = skipToken;
            }
            
            this.writer.EndScope();     // end data array

            if (this.JsonFormatVersion >= 2)
            {
                // $count=inline support
                if (this.RequestDescription.CountOption == RequestQueryCountOption.Inline)
                {
                    this.WriteRowCount();
                }

                // After looping through the objects in the sequence, decide if we need to write the next
                // page link and if yes, write it by invoking the delegate
                if (this.NeedNextPageLink(elements))
                {
                    this.WriteNextPageLink(lastObject, lastExpandedSkipToken, parentUri);
                }

                this.writer.EndScope();     // end object scope
            }

            this.writer.EndScope();     // end "d" wrapper

            this.PopSegmentName(needPop);
        }
 /// <summary>
 /// Gets the skip token object contained in the expanded result for standard paging.
 /// </summary>
 /// <param name="expanded">Current expanded result.</param>
 /// <returns>Skip token object if any.</returns>
 protected IExpandedResult GetSkipToken(IExpandedResult expanded)
 {
     if (expanded != null && !this.IsCustomPaged && !this.RequestDescription.IsRequestForEnumServiceOperation)
     {
         return expanded.GetExpandedPropertyValue(XmlConstants.HttpQueryStringSkipToken) as IExpandedResult;
     }
     
     return null;
 }
 /// <summary>
 /// Writes the next page link to the current xml writer corresponding to the feed
 /// </summary>
 /// <param name="lastElement">Object that will contain the keys for skip token</param>
 /// <param name="skipTokenExpandedResult">The <see cref="IExpandedResult"/> of the $skiptoken property of the object being written</param>
 /// <param name="absoluteUri">Absolute URI for the result</param>
 private void WriteNextPageLink(object lastElement, IExpandedResult skipTokenExpandedResult, Uri absoluteUri)
 {
     this.writer.WriteName(XmlConstants.JsonNextString);
     this.writer.WriteValue(this.GetNextLinkUri(lastElement, skipTokenExpandedResult, absoluteUri));
 }
        /// <summary>Writes multiple top-level elements, possibly none.</summary>
        /// <param name="expanded">Expanded results for elements.</param>
        /// <param name="elements">Enumerator for elements to write.</param>
        protected override void WriteTopLevelElements(IExpandedResult expanded, QueryResultInfo elements)
        {
            Debug.Assert(
                !this.RequestDescription.IsSingleResult,
                "!this.RequestDescription.IsSingleResult -- otherwise WriteTopLevelElement should have been called");

            if (this.RequestDescription.LinkUri)
            {
                bool needPop = this.PushSegmentForRoot();
                this.WriteLinkCollection(elements);
                this.PopSegmentName(needPop);
            }
            else
            {
                MetadataProviderEdmModel model = this.Service.Provider.GetMetadataProviderEdmModel();
                OperationWrapper operation = this.RequestDescription.LastSegmentInfo.Operation;
                
                IEdmOperation edmOperation = model.GetRelatedOperation(operation);
                Debug.Assert(edmOperation != null, "edmOperation != null");

                IEdmCollectionTypeReference collectionType = (IEdmCollectionTypeReference)edmOperation.ReturnType;
                bool isJsonLightResponse = ContentTypeUtil.IsResponseMediaTypeJsonLight(this.Service, /*isEntryOrFeed*/ false);
                this.collectionWriter = this.writer.CreateODataCollectionWriter(isJsonLightResponse ? null : collectionType.ElementType());

                ODataCollectionStart collectionStart = new ODataCollectionStart { Name = this.ComputeContainerName() };
                collectionStart.SetSerializationInfo(new ODataCollectionStartSerializationInfo { CollectionTypeName = collectionType.FullName() });

                this.collectionWriter.WriteStart(collectionStart);
                while (elements.HasMoved)
                {
                    object element = elements.Current;
                    ResourceType resourceType = element == null ?
                        this.RequestDescription.TargetResourceType : WebUtil.GetResourceType(this.Provider, element);
                    if (resourceType == null)
                    {
                        throw new InvalidOperationException(Microsoft.OData.Service.Strings.Serializer_UnsupportedTopLevelType(element.GetType()));
                    }

                    this.collectionWriter.WriteItem(this.GetPropertyValue(XmlConstants.XmlCollectionItemElementName, resourceType, element, false /*openProperty*/).FromODataValue());
                    elements.MoveNext();
                }

                this.collectionWriter.WriteEnd();
                this.collectionWriter.Flush();
            }
        }