SyndicationSerializer.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / ndp / fx / src / DataWeb / Server / System / Data / Services / Serializers / SyndicationSerializer.cs / 3 / 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.Providers;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO; 
    using System.Reflection;
    using System.Reflection.Emit; 
    using System.ServiceModel.Syndication; 
    using System.Text;
    using System.Xml; 
    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); 
            }
        } 
 
        /// 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) 
            {
                this.PushSegmentForRoot();
                this.WriteEntryElement(
                    expanded, 
                    element,
                    this.RequestDescription.TargetElementType, 
                    this.RequestDescription.ResultUri, 
                    this.RequestDescription.ContainerName,
                    this.resultItem); 
                this.PopSegmentName();
            }
            else
            { 
                Debug.Assert(
                    this.RequestDescription.TargetSource == RequestTargetSource.Property, 
                    "TargetSource(" + this.RequestDescription.TargetSource + ") == Property -- otherwise control shouldn't be here."); 
                ResourceType resourcePropertyType;
#if ASTORIA_OPEN_OBJECT 
                if (this.RequestDescription.TargetKind == RequestTargetKind.OpenProperty)
                {
                    Type propertyType = element == null ? typeof(string) : element.GetType();
                    resourcePropertyType = this.Provider.GetResourceType(propertyType); 
                    if (resourcePropertyType == null)
                    { 
                        throw new InvalidOperationException(Strings.Serializer_UnsupportedTopLevelType(propertyType)); 
                    }
                } 
                else
#endif
                {
                    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.");

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

            // 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 (
#if ASTORIA_OPEN_OBJECT 
                this.RequestDescription.TargetKind != RequestTargetKind.OpenProperty &&
#endif 
                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;
            this.PushSegmentForRoot();
            this.WriteFeedElements(
                expanded, 
                elements,
                this.RequestDescription.TargetElementType, 
                title,                                      // title 
                this.RequestDescription.ResultUri,          // absoluteUri
                relativeUri,                                // relativeUri 
                hasMoved,                                   // hasMoved
                this.resultFeed);                           // feed
            this.PopSegmentName();
 
            SyndicationFeedFormatter formatter = this.factory.CreateSyndicationFeedFormatter(this.resultFeed);
            formatter.WriteTo(this.writer); 
        } 

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

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

        /// 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.
        private void WriteComplexObjectValue(object element, string propertyName, ResourceType expectedType, string relativeUri, DictionaryContent content)
        { 
            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.
            DictionaryContent valueProperties = new DictionaryContent(expectedType.Properties.Count); 
            Debug.Assert(!expectedType.Type.IsValueType, "!expectedType.Type.IsValueType -- support for this was removed."); 

            if (element == null) 
            {
                content.AddNull(expectedType.FullName, propertyName);
            }
            else 
            {
                Type elementType = element.GetType(); 
                if (this.AddToComplexTypeCollection(element)) 
                {
                    ResourceType resourceType = this.Provider.GetResourceType(elementType); 
                    content.Add(propertyName, resourceType.FullName, valueProperties);
                    this.WriteObjectProperties(null, element, resourceType, null, relativeUri, null, valueProperties);
                    this.RemoveFromComplexTypeCollection(element);
                } 
                else
                { 
                    throw new InvalidOperationException(Strings.DataServiceException_GeneralError); 
                }
            } 
        }

        /// 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, Type expectedType, Uri absoluteUri, string relativeUri, SyndicationItem target)
        {
            Debug.Assert(expectedType != null, "expectedType != null");
            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; 
            ResourceType resourceType = this.Provider.GetResourceType(expectedType);
            if (resourceType == null)
            {
                Debug.Assert(expectedType == typeof(object), "only object type expected here"); 
                title = expectedType.Name;
                fullName = expectedType.FullName; 
            } 
            else
            { 
                title = resourceType.Name;
                fullName = resourceType.FullName;
            }
 
            if (element == null)
            { 
                SetEntryTypeName(target, fullName); 
                target.AttributeExtensions[QualifiedNullAttribute] = XmlConstants.XmlTrueLiteral;
                this.WriteOtherElements( 
                    title,
                    XmlConstants.AtomEditRelationAttributeValue,
                    absoluteUri,
                    relativeUri, 
                    target);
            } 
            else 
            {
                absoluteUri = Serializer.GetUri(element, this.Provider, this.GetContainerForCurrent(element), this.AbsoluteServiceUri); 
                string[] segments = absoluteUri.Segments;
                Debug.Assert(segments.Length > 0, "segments > 0 -- a path to an entry should have at least one segment");
                relativeUri = segments[segments.Length - 1];
                Type elementType = element.GetType(); 
                resourceType = this.Provider.GetResourceType(elementType);
                SetEntryTypeName(target, resourceType.FullName); 
                this.WriteOtherElements( 
                    title,
                    XmlConstants.AtomEditRelationAttributeValue, 
                    absoluteUri,
                    relativeUri,
                    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(resourceType.Properties.Count); 
                target.Content = content;
                this.WriteObjectProperties(expanded, element, resourceType, absoluteUri, relativeUri, target, content); 
            } 
        }
 
        /// 
        /// 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.
        private void WriteFeedElements(IExpandedResult expanded, IEnumerator elements, Type expectedType, string title, Uri absoluteUri, string relativeUri, bool hasMoved, SyndicationFeed feed)
        { 
            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);
 
            // 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()); 
        }
 
        /// 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. 
        /// An object that can enumerate syndication items. 
        private IEnumerable DeferredFeedItems(IExpandedResult expanded, IEnumerator elements, Type expectedType, bool hasMoved, object activeSegmentNames)
        { 
            object savedSegmentNames = this.SaveSegmentNames();
            this.RestoreSegmentNames(activeSegmentNames);
            while (hasMoved)
            { 
                object o = elements.Current;
                if (o != null) 
                { 
                    SyndicationItem target = new SyndicationItem();
                    if (o is IExpandedResult) 
                    {
                        expanded = (IExpandedResult)o;
                        o = expanded.ExpandedElement;
                    } 

                    this.WriteEntryElement(expanded, o, expectedType, null, null, target); 
                    yield return target; 
                }
 
                hasMoved = elements.MoveNext();
            }

            this.RestoreSegmentNames(savedSegmentNames); 
        }
 
        ///  
        /// Write entry/feed elements, except the content element and related links
        ///  
        /// title for the current element
        /// link relation for the self uri
        /// absolute uri for the current element
        /// relative uri for the current element 
        /// Item to write to.
        private void WriteOtherElements(string title, string linkRelation, Uri absoluteUri, string relativeUri, SyndicationItem item) 
        { 
            Debug.Assert(item != null, "item != null");
            Debug.Assert(relativeUri != null, "relativeUri != null"); 

            item.Id = absoluteUri.AbsoluteUri;
            item.LastUpdatedTime = this.lastUpdatedTime;
            item.Authors.Add(EmptyPerson); 
            item.Title = new TextSyndicationContent(String.Empty);
 
            // Write the link relation element 
            var link = new SyndicationLink();
            link.RelationshipType = linkRelation; 
            link.Title = title;
            link.Uri = new Uri(relativeUri, UriKind.Relative);
            item.Links.Add(link);
        } 

        /// 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. 
        private void WriteObjectProperties(IExpandedResult expanded, object customObject, ResourceType resourceType, Uri absoluteUri, string relativeUri, SyndicationItem item, DictionaryContent content)
        { 
            Debug.Assert(customObject != null, "customObject != null"); 
            Debug.Assert(resourceType != null, "resourceType != null");
            Debug.Assert(resourceType.Type.IsAssignableFrom(customObject.GetType()), "resourceType.Type.IsAssignableFrom(customObject.GetType())"); 
            Debug.Assert(!String.IsNullOrEmpty(relativeUri), "!String.IsNullOrEmpty(relativeUri)");
            Debug.Assert(
                absoluteUri != null || resourceType.ResourceTypeKind != ResourceTypeKind.EntityType,
                "absoluteUri != null || resourceType.ResourceTypeKind != ResourceTypeKind.EntityType"); 

            List navProperties = null; 
            List navPropertyValues = null; 
            if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType)
            { 
                if (this.CurrentContainer.IsEntityDisallowed(resourceType))
                {
                    throw new InvalidOperationException(Strings.BaseServiceProvider_NavigationPropertiesOnDerivedEntityTypesNotSupported(resourceType.FullName, this.CurrentContainer.Name));
                } 

                navProperties = new List(resourceType.Properties.Count); 
                navPropertyValues = new List(resourceType.Properties.Count); 
            }
 
            var action = resourceType.DictionarySerializerDelegate;
            if (action == null)
            {
                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) 
                { 
                    if (property.TypeKind == ResourceTypeKind.Primitive)
                    { 
                        // WritePrimitiveValue(propertyValue, property.Name, property.ResourceType, content);
                        generator.Emit(OpCodes.Dup);
                        generator.Emit(OpCodes.Call, property.GetMethod);
                        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;
            }

            action(customObject, content); 

            foreach (ResourceProperty property in resourceType.Properties) 
            { 
                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(expanded, customObject, property) : null; 
                    navProperties.Add(property);
                    navPropertyValues.Add(propertyValue); 
                } 
                else
                { 
                    if (property.TypeKind == ResourceTypeKind.ComplexType)
                    {
                        object propertyValue = property.GetValue(customObject);
                        this.PushSegmentForProperty(property); 
                        this.WriteComplexObjectValue(propertyValue, propertyName, property.ResourceType, relativeUri + "/" + property.Name, content);
                        this.PopSegmentName(); 
                    } 
                }
            } 

#if ASTORIA_OPEN_OBJECT
            if (resourceType.IsOpenType)
            { 
                IEnumerable> properties = OpenTypeAttribute.EnumerateOpenProperties(customObject);
                foreach (KeyValuePair property in properties) 
                { 
                    string propertyName = property.Key;
                    object value = property.Value; 
                    if (value == null || value == DBNull.Value)
                    {
                        continue;
                    } 

                    Type valueType = value.GetType(); 
                    ResourceType propertyResourceType = this.Provider.GetResourceType(valueType); 
                    if (propertyResourceType == null)
                    { 
                        Type elementType = BaseServiceProvider.GetIEnumerableElement(valueType);
                        if (elementType != null)
                        {
                            // Currently we only support collection of entities 
                            ResourceType collectionType = this.Provider.GetResourceType(elementType);
                            if (collectionType != null && collectionType.ResourceTypeKind == ResourceTypeKind.EntityType) 
                            { 
                                propertyResourceType = collectionType;
                            } 
                            else
                            {
                                // 500 - Internal Server Error
                                ////throw new DataServiceException(500, 
                                ////    Strings.AtomSerializer_OpenPropertyComplexCollectionNotSupported(
                                ////        propertyName, customObject, valueType)); 
                                // If we choose to throw an exception, consider that SByte[] would 
                                // fall into this branch as well, as the array-ness get stripped by
                                // GetIEnumerableElement. 
                                continue;
                            }
                        }
                        else 
                        {
                            // A null ResourceType indicates a ----ed type (eg, IntPtr or DateTimeOffset). 
                            continue; 
                        }
                    } 

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

                            ResourcePropertyKind propertyKind = ResourcePropertyKind.OpenProperty;
                            propertyKind |= (elementType != valueType) ? ResourcePropertyKind.ResourceSetReference : ResourcePropertyKind.ResourceReference;
                            ResourceProperty customProperty = new ResourceProperty( 
                                propertyName,
                                propertyKind, 
                                propertyResourceType); 
                            navProperties.Add(customProperty);
                            navPropertyValues.Add(value); 
                        }
                    }
                }
            } 
#endif
 
            if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType) 
            {
                for (int i = 0; i < navProperties.Count; i++) 
                {
                    ResourceProperty navProperty = navProperties[i];
                    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);
                    bool openCollectionProperty = navProperty.IsOfKind(ResourcePropertyKind.OpenProperty | ResourcePropertyKind.ResourceSetReference);
                    string segmentIdentifier = openCollectionProperty ? navProperty.Name + "()" : navProperty.Name;
 
                    if (!this.ShouldExpandSegment(navProperty.Name))
                    { 
                        WriteDeferredContentElement( 
                            XmlConstants.DataWebRelatedNamespace + navProperty.Name,
                            navProperty.Name, 
                            relativeUri + "/" + segmentIdentifier,
                            linkType,
                            item);
                    } 
                    else
                    { 
                        object propertyValue = navPropertyValues[i]; 
                        object expandedPropertyValue =
                            (propertyValue is IExpandedResult) ? 
                            ((IExpandedResult)propertyValue).ExpandedElement :
                            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);

                        this.PushSegmentForProperty(navProperty); 

                        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");

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

                        this.PopSegmentName(); 
                    }
                }
            }
        } 

        #region Inner types. 
 
        /// 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.
//---------------------------------------------------------------------- 
// 
//      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.Providers;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO; 
    using System.Reflection;
    using System.Reflection.Emit; 
    using System.ServiceModel.Syndication; 
    using System.Text;
    using System.Xml; 
    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); 
            }
        } 
 
        /// 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) 
            {
                this.PushSegmentForRoot();
                this.WriteEntryElement(
                    expanded, 
                    element,
                    this.RequestDescription.TargetElementType, 
                    this.RequestDescription.ResultUri, 
                    this.RequestDescription.ContainerName,
                    this.resultItem); 
                this.PopSegmentName();
            }
            else
            { 
                Debug.Assert(
                    this.RequestDescription.TargetSource == RequestTargetSource.Property, 
                    "TargetSource(" + this.RequestDescription.TargetSource + ") == Property -- otherwise control shouldn't be here."); 
                ResourceType resourcePropertyType;
#if ASTORIA_OPEN_OBJECT 
                if (this.RequestDescription.TargetKind == RequestTargetKind.OpenProperty)
                {
                    Type propertyType = element == null ? typeof(string) : element.GetType();
                    resourcePropertyType = this.Provider.GetResourceType(propertyType); 
                    if (resourcePropertyType == null)
                    { 
                        throw new InvalidOperationException(Strings.Serializer_UnsupportedTopLevelType(propertyType)); 
                    }
                } 
                else
#endif
                {
                    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.");

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

            // 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 (
#if ASTORIA_OPEN_OBJECT 
                this.RequestDescription.TargetKind != RequestTargetKind.OpenProperty &&
#endif 
                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;
            this.PushSegmentForRoot();
            this.WriteFeedElements(
                expanded, 
                elements,
                this.RequestDescription.TargetElementType, 
                title,                                      // title 
                this.RequestDescription.ResultUri,          // absoluteUri
                relativeUri,                                // relativeUri 
                hasMoved,                                   // hasMoved
                this.resultFeed);                           // feed
            this.PopSegmentName();
 
            SyndicationFeedFormatter formatter = this.factory.CreateSyndicationFeedFormatter(this.resultFeed);
            formatter.WriteTo(this.writer); 
        } 

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

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

        /// 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.
        private void WriteComplexObjectValue(object element, string propertyName, ResourceType expectedType, string relativeUri, DictionaryContent content)
        { 
            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.
            DictionaryContent valueProperties = new DictionaryContent(expectedType.Properties.Count); 
            Debug.Assert(!expectedType.Type.IsValueType, "!expectedType.Type.IsValueType -- support for this was removed."); 

            if (element == null) 
            {
                content.AddNull(expectedType.FullName, propertyName);
            }
            else 
            {
                Type elementType = element.GetType(); 
                if (this.AddToComplexTypeCollection(element)) 
                {
                    ResourceType resourceType = this.Provider.GetResourceType(elementType); 
                    content.Add(propertyName, resourceType.FullName, valueProperties);
                    this.WriteObjectProperties(null, element, resourceType, null, relativeUri, null, valueProperties);
                    this.RemoveFromComplexTypeCollection(element);
                } 
                else
                { 
                    throw new InvalidOperationException(Strings.DataServiceException_GeneralError); 
                }
            } 
        }

        /// 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, Type expectedType, Uri absoluteUri, string relativeUri, SyndicationItem target)
        {
            Debug.Assert(expectedType != null, "expectedType != null");
            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; 
            ResourceType resourceType = this.Provider.GetResourceType(expectedType);
            if (resourceType == null)
            {
                Debug.Assert(expectedType == typeof(object), "only object type expected here"); 
                title = expectedType.Name;
                fullName = expectedType.FullName; 
            } 
            else
            { 
                title = resourceType.Name;
                fullName = resourceType.FullName;
            }
 
            if (element == null)
            { 
                SetEntryTypeName(target, fullName); 
                target.AttributeExtensions[QualifiedNullAttribute] = XmlConstants.XmlTrueLiteral;
                this.WriteOtherElements( 
                    title,
                    XmlConstants.AtomEditRelationAttributeValue,
                    absoluteUri,
                    relativeUri, 
                    target);
            } 
            else 
            {
                absoluteUri = Serializer.GetUri(element, this.Provider, this.GetContainerForCurrent(element), this.AbsoluteServiceUri); 
                string[] segments = absoluteUri.Segments;
                Debug.Assert(segments.Length > 0, "segments > 0 -- a path to an entry should have at least one segment");
                relativeUri = segments[segments.Length - 1];
                Type elementType = element.GetType(); 
                resourceType = this.Provider.GetResourceType(elementType);
                SetEntryTypeName(target, resourceType.FullName); 
                this.WriteOtherElements( 
                    title,
                    XmlConstants.AtomEditRelationAttributeValue, 
                    absoluteUri,
                    relativeUri,
                    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(resourceType.Properties.Count); 
                target.Content = content;
                this.WriteObjectProperties(expanded, element, resourceType, absoluteUri, relativeUri, target, content); 
            } 
        }
 
        /// 
        /// 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.
        private void WriteFeedElements(IExpandedResult expanded, IEnumerator elements, Type expectedType, string title, Uri absoluteUri, string relativeUri, bool hasMoved, SyndicationFeed feed)
        { 
            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);
 
            // 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()); 
        }
 
        /// 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. 
        /// An object that can enumerate syndication items. 
        private IEnumerable DeferredFeedItems(IExpandedResult expanded, IEnumerator elements, Type expectedType, bool hasMoved, object activeSegmentNames)
        { 
            object savedSegmentNames = this.SaveSegmentNames();
            this.RestoreSegmentNames(activeSegmentNames);
            while (hasMoved)
            { 
                object o = elements.Current;
                if (o != null) 
                { 
                    SyndicationItem target = new SyndicationItem();
                    if (o is IExpandedResult) 
                    {
                        expanded = (IExpandedResult)o;
                        o = expanded.ExpandedElement;
                    } 

                    this.WriteEntryElement(expanded, o, expectedType, null, null, target); 
                    yield return target; 
                }
 
                hasMoved = elements.MoveNext();
            }

            this.RestoreSegmentNames(savedSegmentNames); 
        }
 
        ///  
        /// Write entry/feed elements, except the content element and related links
        ///  
        /// title for the current element
        /// link relation for the self uri
        /// absolute uri for the current element
        /// relative uri for the current element 
        /// Item to write to.
        private void WriteOtherElements(string title, string linkRelation, Uri absoluteUri, string relativeUri, SyndicationItem item) 
        { 
            Debug.Assert(item != null, "item != null");
            Debug.Assert(relativeUri != null, "relativeUri != null"); 

            item.Id = absoluteUri.AbsoluteUri;
            item.LastUpdatedTime = this.lastUpdatedTime;
            item.Authors.Add(EmptyPerson); 
            item.Title = new TextSyndicationContent(String.Empty);
 
            // Write the link relation element 
            var link = new SyndicationLink();
            link.RelationshipType = linkRelation; 
            link.Title = title;
            link.Uri = new Uri(relativeUri, UriKind.Relative);
            item.Links.Add(link);
        } 

        /// 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. 
        private void WriteObjectProperties(IExpandedResult expanded, object customObject, ResourceType resourceType, Uri absoluteUri, string relativeUri, SyndicationItem item, DictionaryContent content)
        { 
            Debug.Assert(customObject != null, "customObject != null"); 
            Debug.Assert(resourceType != null, "resourceType != null");
            Debug.Assert(resourceType.Type.IsAssignableFrom(customObject.GetType()), "resourceType.Type.IsAssignableFrom(customObject.GetType())"); 
            Debug.Assert(!String.IsNullOrEmpty(relativeUri), "!String.IsNullOrEmpty(relativeUri)");
            Debug.Assert(
                absoluteUri != null || resourceType.ResourceTypeKind != ResourceTypeKind.EntityType,
                "absoluteUri != null || resourceType.ResourceTypeKind != ResourceTypeKind.EntityType"); 

            List navProperties = null; 
            List navPropertyValues = null; 
            if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType)
            { 
                if (this.CurrentContainer.IsEntityDisallowed(resourceType))
                {
                    throw new InvalidOperationException(Strings.BaseServiceProvider_NavigationPropertiesOnDerivedEntityTypesNotSupported(resourceType.FullName, this.CurrentContainer.Name));
                } 

                navProperties = new List(resourceType.Properties.Count); 
                navPropertyValues = new List(resourceType.Properties.Count); 
            }
 
            var action = resourceType.DictionarySerializerDelegate;
            if (action == null)
            {
                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) 
                { 
                    if (property.TypeKind == ResourceTypeKind.Primitive)
                    { 
                        // WritePrimitiveValue(propertyValue, property.Name, property.ResourceType, content);
                        generator.Emit(OpCodes.Dup);
                        generator.Emit(OpCodes.Call, property.GetMethod);
                        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;
            }

            action(customObject, content); 

            foreach (ResourceProperty property in resourceType.Properties) 
            { 
                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(expanded, customObject, property) : null; 
                    navProperties.Add(property);
                    navPropertyValues.Add(propertyValue); 
                } 
                else
                { 
                    if (property.TypeKind == ResourceTypeKind.ComplexType)
                    {
                        object propertyValue = property.GetValue(customObject);
                        this.PushSegmentForProperty(property); 
                        this.WriteComplexObjectValue(propertyValue, propertyName, property.ResourceType, relativeUri + "/" + property.Name, content);
                        this.PopSegmentName(); 
                    } 
                }
            } 

#if ASTORIA_OPEN_OBJECT
            if (resourceType.IsOpenType)
            { 
                IEnumerable> properties = OpenTypeAttribute.EnumerateOpenProperties(customObject);
                foreach (KeyValuePair property in properties) 
                { 
                    string propertyName = property.Key;
                    object value = property.Value; 
                    if (value == null || value == DBNull.Value)
                    {
                        continue;
                    } 

                    Type valueType = value.GetType(); 
                    ResourceType propertyResourceType = this.Provider.GetResourceType(valueType); 
                    if (propertyResourceType == null)
                    { 
                        Type elementType = BaseServiceProvider.GetIEnumerableElement(valueType);
                        if (elementType != null)
                        {
                            // Currently we only support collection of entities 
                            ResourceType collectionType = this.Provider.GetResourceType(elementType);
                            if (collectionType != null && collectionType.ResourceTypeKind == ResourceTypeKind.EntityType) 
                            { 
                                propertyResourceType = collectionType;
                            } 
                            else
                            {
                                // 500 - Internal Server Error
                                ////throw new DataServiceException(500, 
                                ////    Strings.AtomSerializer_OpenPropertyComplexCollectionNotSupported(
                                ////        propertyName, customObject, valueType)); 
                                // If we choose to throw an exception, consider that SByte[] would 
                                // fall into this branch as well, as the array-ness get stripped by
                                // GetIEnumerableElement. 
                                continue;
                            }
                        }
                        else 
                        {
                            // A null ResourceType indicates a ----ed type (eg, IntPtr or DateTimeOffset). 
                            continue; 
                        }
                    } 

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

                            ResourcePropertyKind propertyKind = ResourcePropertyKind.OpenProperty;
                            propertyKind |= (elementType != valueType) ? ResourcePropertyKind.ResourceSetReference : ResourcePropertyKind.ResourceReference;
                            ResourceProperty customProperty = new ResourceProperty( 
                                propertyName,
                                propertyKind, 
                                propertyResourceType); 
                            navProperties.Add(customProperty);
                            navPropertyValues.Add(value); 
                        }
                    }
                }
            } 
#endif
 
            if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType) 
            {
                for (int i = 0; i < navProperties.Count; i++) 
                {
                    ResourceProperty navProperty = navProperties[i];
                    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);
                    bool openCollectionProperty = navProperty.IsOfKind(ResourcePropertyKind.OpenProperty | ResourcePropertyKind.ResourceSetReference);
                    string segmentIdentifier = openCollectionProperty ? navProperty.Name + "()" : navProperty.Name;
 
                    if (!this.ShouldExpandSegment(navProperty.Name))
                    { 
                        WriteDeferredContentElement( 
                            XmlConstants.DataWebRelatedNamespace + navProperty.Name,
                            navProperty.Name, 
                            relativeUri + "/" + segmentIdentifier,
                            linkType,
                            item);
                    } 
                    else
                    { 
                        object propertyValue = navPropertyValues[i]; 
                        object expandedPropertyValue =
                            (propertyValue is IExpandedResult) ? 
                            ((IExpandedResult)propertyValue).ExpandedElement :
                            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);

                        this.PushSegmentForProperty(navProperty); 

                        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");

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

                        this.PopSegmentName(); 
                    }
                }
            }
        } 

        #region Inner types. 
 
        /// 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