SyndicationSerializer.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataWeb / Server / System / Data / Services / Serializers / SyndicationSerializer.cs / 1305376 / SyndicationSerializer.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
//      Provides a serializer that creates syndication objects and
//      then formatts them. 
//  
//
// @owner [....] 
//---------------------------------------------------------------------

namespace System.Data.Services.Serializers
{ 
    #region Namespaces.
 
    using System; 
    using System.Collections;
    using System.Collections.Generic; 
    using System.Data.Services.Common;
    using System.Data.Services.Providers;
    using System.Diagnostics;
    using System.Globalization; 
    using System.IO;
    using System.Linq; 
    using System.Reflection; 
    using System.Reflection.Emit;
    using System.ServiceModel.Syndication; 
    using System.Text;
    using System.Xml;
    using System.Xml.Linq;
    using System.Xml.Serialization; 

    #endregion Namespaces. 
 
    /// Serializes results into System.ServiceModel.Syndication objects, which can then be formatted.
    internal sealed class SyndicationSerializer : Serializer 
    {
        #region Fields.

        /// Namespace-qualified attribute for null value annotations. 
        internal static readonly XmlQualifiedName QualifiedNullAttribute = new XmlQualifiedName(XmlConstants.AtomNullAttributeName, XmlConstants.DataWebMetadataNamespace);
 
        /// Empty person singleton. 
        private static readonly SyndicationPerson EmptyPerson = new SyndicationPerson(null, String.Empty, null);
 
        /// Namespace-qualified namespace prefix for the DataWeb namespace.
        private static readonly XmlQualifiedName QualifiedDataWebPrefix = new XmlQualifiedName(XmlConstants.DataWebNamespacePrefix, XmlConstants.XmlNamespacesNamespace);

        /// Namespace-qualified namespace prefix for the DataWebMetadata namespace. 
        private static readonly XmlQualifiedName QualifiedDataWebMetadataPrefix = new XmlQualifiedName(XmlConstants.DataWebMetadataNamespacePrefix, XmlConstants.XmlNamespacesNamespace);
 
        /// Factory for syndication formatter implementation. 
        private readonly SyndicationFormatterFactory factory;
 
        /// Last updated time for  elements.
        /// 
        /// While this is currently an arbitrary decision, it at least saves us from re-querying the system time
        /// every time an item is generated. 
        /// 
        private readonly DateTimeOffset lastUpdatedTime = DateTimeOffset.UtcNow; 
 
        /// Writer to which output is sent.
        private readonly XmlWriter writer; 

        /// Top-level feed being built.
        private SyndicationFeed resultFeed;
 
        /// Top-level item being built.
        private SyndicationItem resultItem; 
 
        #endregion Fields.
 
        /// Initializes a new SyndicationSerializer instance.
        /// Request description.
        /// Absolute URI to the service entry point.
        /// Service with configuration and provider from which metadata should be gathered. 
        /// Stream to write to.
        /// Encoding for text in output stream. 
        /// HTTP ETag header value. 
        /// Factory for formatter objects.
        internal SyndicationSerializer( 
            RequestDescription requestDescription,
            Uri absoluteServiceUri,
            IDataService service,
            Stream output, 
            Encoding encoding,
            string etag, 
            SyndicationFormatterFactory factory) 
            : base(requestDescription, absoluteServiceUri, service, etag)
        { 
            Debug.Assert(service != null, "service != null");
            Debug.Assert(output != null, "output != null");
            Debug.Assert(encoding != null, "encoding != null");
            Debug.Assert(factory != null, "factory != null"); 

            this.factory = factory; 
            this.writer = factory.CreateWriter(output, encoding); 
        }
 
        /// Serializes exception information.
        /// Description of exception to serialize.
        public override void WriteException(HandleExceptionArgs args)
        { 
            ErrorHandler.SerializeXmlError(args, this.writer);
        } 
 
        /// Writes a primitive value to the specified output.
        /// Primitive value to write. 
        /// name of the property whose value needs to be written
        /// Type name of the property
        /// Content dictionary to which the value should be written.
        internal static void WritePrimitiveValue(object primitive, string propertyName, string expectedTypeName, DictionaryContent content) 
        {
            Debug.Assert(!String.IsNullOrEmpty(propertyName), "!String.IsNullOrEmpty(propertyName)"); 
            Debug.Assert(expectedTypeName != null, "expectedTypeName != null"); 
            if (primitive == null)
            { 
                content.AddNull(expectedTypeName, propertyName);
            }
            else
            { 
                string primitiveString = PlainXmlSerializer.PrimitiveToString(primitive);
                Debug.Assert(primitiveString != null, "primitiveString != null"); 
                content.Add(propertyName, expectedTypeName, primitiveString); 
            }
        } 

        /// Writes an Atom link element.
        /// relation of the link element with the parent element
        /// title of the deferred element 
        /// uri for the deferred element
        /// link type for the deferred element 
        /// Item to write link in. 
        internal static void WriteDeferredContentElement(string linkRelation, string title, string href, string linkType, SyndicationItem item)
        { 
            Debug.Assert(linkRelation != null, "linkRelation != null");
            Debug.Assert(item != null, "item != null");
            Debug.Assert(linkType != null, "linkType != null");
 
            SyndicationLink link = new SyndicationLink();
            link.RelationshipType = linkRelation; 
            link.Title = title; 
            link.Uri = new Uri(href, UriKind.RelativeOrAbsolute);
            link.MediaType = linkType; 
            item.Links.Add(link);
        }

        /// Flushes the writer to the underlying stream. 
        protected override void Flush()
        { 
            this.writer.Flush(); 
        }
 
        /// Writes a single top-level element.
        /// Expanded properties for the result.
        /// Element to write, possibly null.
        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);
        }
 
        /// Writes multiple top-level elements, possibly none.
        /// Expanded properties for the result. 
        /// Enumerator for elements to write. 
        /// Whether  was succesfully advanced to the first element.
        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); 
        }
 
        /// 
        /// Write out the entry count
        /// 
        protected override void WriteRowCount() 
        {
            XElement rowCountElement = new XElement( 
                XName.Get(XmlConstants.RowCountElement, XmlConstants.DataWebMetadataNamespace), 
                RequestDescription.CountValue);
 
            this.resultFeed.ElementExtensions.Add(rowCountElement);
        }

        ///  
        /// Write out the uri for the given element
        ///  
        /// element whose uri needs to be written out. 
        protected override void WriteLink(object element)
        { 
            throw Error.NotImplemented();
        }

        ///  
        /// Write out the uri for the given elements
        ///  
        /// elements whose uri need to be writtne out 
        /// the current state of the enumerator.
        protected override void WriteLinkCollection(IEnumerator elements, bool hasMoved) 
        {
            throw Error.NotImplemented();
        }
 
        /// Ensures that common namespaces are included in the topmost tag.
        /// Attribute extensions to write namespaces to. 
        ///  
        /// This method should be called by any method that may write a
        /// topmost element tag. 
        /// 
        private static void IncludeCommonNamespaces(Dictionary attributeExtensions)
        {
            attributeExtensions.Add(QualifiedDataWebPrefix, XmlConstants.DataWebNamespace); 
            attributeExtensions.Add(QualifiedDataWebMetadataPrefix, XmlConstants.DataWebMetadataNamespace);
        } 
 
        /// Sets the type name for the specified syndication entry.
        /// Item on which to set the type name. 
        /// Full type name for the entry.
        private static void SetEntryTypeName(SyndicationItem item, string fullName)
        {
            Debug.Assert(item != null, "item != null"); 
            item.Categories.Add(new SyndicationCategory(fullName, XmlConstants.DataWebSchemeNamespace, null));
        } 
 
        /// 
        /// Write the link relation element 
        /// 
        /// title for the current element
        /// link relation for the self uri
        /// relative uri for the current element 
        /// Item to write to.
        /// List of custom attributes to add to the link element 
        private static void WriteLinkRelations(string title, string linkRelation, string relativeUri, SyndicationItem item, params KeyValuePair[] attributeExtensions) 
        {
            Debug.Assert(item != null, "item != null"); 
            Debug.Assert(relativeUri != null, "relativeUri != null");

            // Write the link relation element
            var link = new SyndicationLink(); 
            link.RelationshipType = linkRelation;
            link.Title = title; 
            link.Uri = new Uri(relativeUri, UriKind.Relative); 
            foreach (KeyValuePair attributeExtension in attributeExtensions)
            { 
                link.AttributeExtensions.Add(attributeExtension.Key, attributeExtension.Value);
            }

            item.Links.Add(link); 
        }
 
        ///  
        /// Checks if a particular property value should be skipped from the content section due to
        /// EntityProperty mappings for friendly feeds 
        /// 
        /// Current root segment in the source tree for a resource type
        /// Name of the property being checked for
        /// true if skipping of property value is needed, false otherwise 
        private static bool EpmNeedToSkip(EpmSourcePathSegment currentSourceRoot, String propertyName)
        { 
            if (currentSourceRoot != null) 
            {
                EpmSourcePathSegment epmProperty = currentSourceRoot.SubProperties.Find(subProp => subProp.PropertyName == propertyName); 
                if (epmProperty != null)
                {
                    Debug.Assert(epmProperty.SubProperties.Count == 0, "Complex type added as leaf node in EPM tree.");
                    Debug.Assert(epmProperty.EpmInfo != null, "Found a non-leaf property for which EpmInfo is not set."); 
                    Debug.Assert(epmProperty.EpmInfo.Attribute != null, "Attribute should always be initialized for EpmInfo.");
                    if (epmProperty.EpmInfo.Attribute.KeepInContent == false) 
                    { 
                        return true;
                    } 
                }
            }

            return false; 
        }
 
        ///  
        /// Obtains the child EPM segment corresponding to the given 
        ///  
        /// Current root segment
        /// Name of property
        /// Child segment or null if there is not segment corresponding to the given 
        private static EpmSourcePathSegment EpmGetComplexPropertySegment(EpmSourcePathSegment currentSourceRoot, String propertyName) 
        {
            if (currentSourceRoot != null) 
            { 
                return currentSourceRoot.SubProperties.Find(subProp => subProp.PropertyName == propertyName);
            } 
            else
            {
                return null;
            } 
        }
 
        /// Writes the value of a complex object. 
        /// Element to write.
        /// name of the property whose value needs to be written 
        /// expected type of the property
        /// relative uri for the complex type element
        /// Content to write to.
        /// Epm source sub-tree corresponding to  
        private void WriteComplexObjectValue(object element, string propertyName, ResourceType expectedType, string relativeUri, DictionaryContent content, EpmSourcePathSegment currentSourceRoot)
        { 
            Debug.Assert(!String.IsNullOrEmpty(propertyName), "!String.IsNullOrEmpty(propertyName)"); 
            Debug.Assert(expectedType != null, "expectedType != null");
            Debug.Assert(!String.IsNullOrEmpty(relativeUri), "!String.IsNullOrEmpty(relativeUri)"); 
            Debug.Assert(expectedType.ResourceTypeKind == ResourceTypeKind.ComplexType, "Must be complex type");
            Debug.Assert(content != null, "content != null");

            // 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. 
            Debug.Assert(!expectedType.IsMediaLinkEntry, "!expectedType.IsMediaLinkEntry");
            DictionaryContent valueProperties = new DictionaryContent(expectedType.Properties.Count); 
            Debug.Assert(!expectedType.InstanceType.IsValueType, "!expectedType.Type.IsValueType -- checked in the resource type constructor.");

            if (element == null)
            { 
                content.AddNull(expectedType.FullName, propertyName);
            } 
            else 
            {
                if (this.AddToComplexTypeCollection(element)) 
                {
                    ResourceType resourceType = WebUtil.GetNonPrimitiveResourceType(this.Provider, element);
                    this.WriteObjectProperties(null, element, resourceType, null, relativeUri, null, valueProperties, currentSourceRoot);
                    if (!valueProperties.IsEmpty) 
                    {
                        content.Add(propertyName, resourceType.FullName, valueProperties); 
                    } 

                    this.RemoveFromComplexTypeCollection(element); 
                }
                else
                {
                    throw new InvalidOperationException(Strings.Serializer_LoopsNotAllowedInComplexTypes(propertyName)); 
                }
            } 
        } 

        /// Write the entry element. 
        /// Expanded result provider for the specified .
        /// element representing the entry element
        /// expected type of the entry element
        /// absolute uri for the entry element 
        /// relative uri for the entry element
        /// Target to write to. 
        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  
                    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  node as SyndicationElementExtension. 
                        target.ElementExtensions.Add(content.GetPropertyContentsReader()); 
                    }
                } 
                else
                {
                    target.Content = content;
                } 
            }
 
#if ASTORIA_FF_CALLBACKS 
            this.Service.InternalOnWriteItem(target, element);
#endif 
        }

        /// 
        /// Writes the feed element for the atom payload 
        /// 
        /// Expanded properties for the result. 
        /// collection of entries in the feed element 
        /// expectedType of the elements in the collection
        /// title of the feed element 
        /// absolute uri representing the feed element
        /// relative uri representing the feed element
        /// whether the enumerator has successfully moved to the first element
        /// Feed to write to. 
        /// 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. 
        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 
        }

        /// 
        /// Writes the next page link to the current xml writer corresponding to the feed 
        /// 
        /// Object that will contain the keys for skip token 
        /// The  of the $skiptoken property of the object being written 
        /// Absolute URI for the result
        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();
        } 
 
        /// Provides an enumeration of deferred feed items.
        /// Expanded properties for the result. 
        /// Elements to enumerate.
        /// Expected type of elements.
        /// Whether the enumerator moved to the first element.
        /// The segment names active at this point in serialization. 
        /// Delegate that writes the next page link if necessity arises
        /// If set to true the function should dispose the elements enumerator (always). 
        /// An object that can enumerate syndication items. 
        private IEnumerable DeferredFeedItems(
            IExpandedResult expanded, 
            IEnumerator elements,
            ResourceType expectedType,
            bool hasMoved,
            object activeSegmentNames, 
            Action 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); 
                }
            } 
        }

        /// 
        /// Write entry/feed elements, except the content element and related links 
        /// 
        /// entity instance being serialized 
        /// resource type of the entry element 
        /// title for the current element
        /// absolute uri for the current element 
        /// relative uri for the current element
        /// entity tag for the Media Resource
        /// Item to write to.
        private void WriteOtherElements(object element, ResourceType type, string title, Uri absoluteUri, string relativeUri, string mediaETag, SyndicationItem item) 
        {
            Debug.Assert(item != null, "item != null"); 
            Debug.Assert(absoluteUri != null, "absoluteUri != null"); 
            Debug.Assert(relativeUri != null, "relativeUri != null");
 
            // Write Id element
            item.Id = absoluteUri.AbsoluteUri;

            // Write Updated element 
            item.LastUpdatedTime = this.lastUpdatedTime;
 
            // Write "edit-media" link 
            if (type != null && type.IsMediaLinkEntry)
            { 
                KeyValuePair[] attributeExtensions = new KeyValuePair[0];
                if (element != null && !string.IsNullOrEmpty(mediaETag))
                {
                    XmlQualifiedName mediaResourceETagKey = new XmlQualifiedName(XmlConstants.AtomETagAttributeName, XmlConstants.DataWebMetadataNamespace); 
                    attributeExtensions = new KeyValuePair[] { new KeyValuePair(mediaResourceETagKey, mediaETag) };
                } 
 
                WriteLinkRelations(
                    title, 
                    XmlConstants.AtomEditMediaRelationAttributeValue,
                    DataServiceStreamProviderWrapper.GetStreamEditMediaUri(relativeUri),
                    item,
                    attributeExtensions); 
            }
 
            // Write "edit" link 
            WriteLinkRelations(
                title, 
                XmlConstants.AtomEditRelationAttributeValue,
                relativeUri,
                item);
        } 

        /// Writes all the properties of the specified resource or complex object. 
        /// Expanded properties for the result. 
        /// Resource or complex object with properties to write out.
        /// resourceType containing metadata about the current custom object 
        /// absolute uri for the given resource
        /// relative uri for the given resource
        /// Item in which to place links / expansions.
        /// Content in which to place values. 
        /// Epm source sub-tree corresponding to 
        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 navProperties = null;
                IEnumerable 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(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)method.CreateDelegate(typeof(Action), 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> properties = this.Provider.GetOpenPropertyValues(customObject); 
                        foreach (KeyValuePair 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(); 
            }
        }

        #region Inner types. 

        /// Stores the resource property, its value and a flag which indicates whether this is a open property or not. 
        private struct ResourcePropertyInfo 
        {
            /// refers to the property that this instance represents. 
            private ResourceProperty resourceProperty;

            /// Value of the property.
            private object value; 

            ///  
            /// Creates a new instance of ResourcePropertyInfo. 
            /// 
            /// resource property instance. 
            /// value for the resource property.
            public ResourcePropertyInfo(ResourceProperty resourceProperty, object value)
            {
                Debug.Assert(resourceProperty != null, "resourceProperty != null"); 
                this.resourceProperty = resourceProperty;
                this.value = value; 
            } 

            /// Returns the resource property. 
            internal ResourceProperty Property
            {
                get { return this.resourceProperty; }
            } 

            /// Returns the value of the resource property. 
            internal object Value 
            {
                get { return this.value; } 
            }
        }

        /// Wrapper for an inline item. 
        [XmlRoot(ElementName = XmlConstants.AtomInlineElementName, Namespace = XmlConstants.DataWebMetadataNamespace)]
        internal class InlineAtomItem : IXmlSerializable 
        { 
            /// Factory for item formatter.
            private readonly SyndicationFormatterFactory factory; 

            /// Item being serialized.
            private SyndicationItem item;
 
            /// Empty constructor.
            internal InlineAtomItem() 
            { 
            }
 
            /// Initializing constructor.
            /// Item being serialized.
            /// Factory for item formatter.
            internal InlineAtomItem(SyndicationItem item, SyndicationFormatterFactory factory) 
            {
                this.item = item; 
                this.factory = factory; 
            }
 
            #region IXmlSerializable Members

            /// Reserved method.
            /// null 
            public System.Xml.Schema.XmlSchema GetSchema()
            { 
                return null; 
            }
 
            /// Generates an object from its XML representation.
            /// XmlReader with representation.
            public void ReadXml(XmlReader reader)
            { 
                throw Error.NotImplemented();
            } 
 
            /// Converts an object into its XML representation.
            /// Writer to write representation into. 
            public void WriteXml(XmlWriter writer)
            {
                SyndicationItemFormatter formatter = this.factory.CreateSyndicationItemFormatter(this.item);
                if (formatter != null) 
                {
                    formatter.WriteTo(writer); 
                } 
            }
 
            #endregion
        }

        /// Wrapper for an inline feed. 
        [XmlRoot(ElementName = XmlConstants.AtomInlineElementName, Namespace = XmlConstants.DataWebMetadataNamespace)]
        internal class InlineAtomFeed : IXmlSerializable 
        { 
            /// Factory for item formatter.
            private readonly SyndicationFormatterFactory factory; 

            /// Feed being serialized.
            private SyndicationFeed feed;
 
            /// Empty constructor.
            internal InlineAtomFeed() 
            { 
            }
 
            /// Initializing constructor.
            /// Feed being serialized.
            /// Factory for item formatter.
            internal InlineAtomFeed(SyndicationFeed feed, SyndicationFormatterFactory factory) 
            {
                this.feed = feed; 
                this.factory = factory; 
            }
 
            #region IXmlSerializable Members

            /// Reserved method.
            /// null 
            public System.Xml.Schema.XmlSchema GetSchema()
            { 
                return null; 
            }
 
            /// Generates an object from its XML representation.
            /// XmlReader with representation.
            public void ReadXml(XmlReader reader)
            { 
                throw Error.NotImplemented();
            } 
 
            /// Converts an object into its XML representation.
            /// Writer to write representation into. 
            public void WriteXml(XmlWriter writer)
            {
                this.factory.CreateSyndicationFeedFormatter(this.feed).WriteTo(writer);
            } 

            #endregion 
        } 

        #endregion Inner types. 
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK