MaterializeFromAtom.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / ndp / fx / src / DataWeb / Client / System / Data / Services / Client / MaterializeFromAtom.cs / 1 / MaterializeFromAtom.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
// materialize objects from an xml stream
//  
//--------------------------------------------------------------------- 

namespace System.Data.Services.Client 
{
    using System;
    using System.Collections;
    using System.Collections.Generic; 
    using System.Diagnostics;
    using System.Linq; 
    using System.Xml; 
    using System.Xml.Linq;
 
    /// 
    /// materialize objects from an application/atom+xml stream
    /// 
    internal class MaterializeAtom : IDisposable, IEnumerable, IEnumerator 
    {
        /// MergeOption captured from context so its static for the entire materialization 
        internal readonly MergeOption MergeOptionValue; 

        /// Options when deserializing properties to the target type. 
        private readonly bool ignoreMissingProperties;

        /// Backreference to the context to allow type resolution.
        private readonly DataServiceContext context; 

        /// DataNamespace captured from context so its static for the entire materialization 
        /// reference equality is important here 
        private readonly string dataNamespace;
 
        /// base type of the object to create from the stream.
        private readonly Type elementType;

        /// when materializing a known type (like string) 
        /// <property> text-value </property>
        private readonly bool expectingSingleValue; 
 
        /// source reader (may be running on top of a buffer or be the real reader)
        private XmlReader reader; 

        /// untyped current object
        private object current;
 
        /// object being updated
        private object inserting; 
 
        /// identity of new objects and associated etag per enumerator.MoveNext()
        private Dictionary identityStack; 

        /// list of links to add
        private List links;
 
        /// debugging tool, # calls to ReadNext()
        private int version; 
 
        /// has GetEnumerator been called?
        private bool calledGetEnumerator; 

        /// stack of readers used when buffering intermediate entries (to create XElements and fire events)
        private Stack stackedReaders;
 
        /// 
        /// output writer, set using reflection 
        ///  
#if DEBUG && !ASTORIA_LIGHT
        private System.IO.TextWriter writer = new System.IO.StringWriter(System.Globalization.CultureInfo.InvariantCulture); 
#else
#pragma warning disable 649
        private System.IO.TextWriter writer;
#pragma warning restore 649 
#endif
 
        ///  
        /// constructor
        ///  
        /// originating context
        /// reader
        /// element base type
        /// merge option to use for this materialization pass 
        internal MaterializeAtom(DataServiceContext context, XmlReader reader, Type elementType, MergeOption mergeOption)
        { 
            Debug.Assert(null != reader, "null XmlReader"); 
            this.context = context;
            this.dataNamespace = reader.Settings.NameTable.Add(context.DataNamespace); 

            this.elementType = elementType;
            this.MergeOptionValue = mergeOption;
            this.ignoreMissingProperties = context.IgnoreMissingProperties; 
            this.reader = reader;
            this.expectingSingleValue = ClientConvert.IsKnownType(Nullable.GetUnderlyingType(elementType) ?? elementType); 
        } 

        /// atom parser state machine positions 
        private enum AtomParseState
        {
            /// None
            None, 

            /// Feed 
            Feed, 

            /// Entry 
            Entry,

            /// Content
            Content, 

            /// Complex 
            Complex, 

            /// Property 
            PropertyContent,

            /// Property
            PropertyComplex, // SQLBUDT 572585 

            /// LinkFeed 
            LinkFeed, 

            /// LinkEntry 
            LinkEntry,
        }

        #region Current 
        /// 
        /// untyped current object property 
        ///  
        public object Current
        { 
            get
            {
                return ClientConvert.VerifyCast(this.elementType, this.current);
            } 
        }
        #endregion 
 
        #region IDisposable
        ///  
        /// dispose
        /// 
        public void Dispose()
        { 
            this.current = null;
            this.inserting = null; 
 
            if (null != this.reader)
            { 
                ((IDisposable)this.reader).Dispose();
            }

            if (null != this.writer) 
            {
                this.writer.Dispose(); 
            } 
        }
        #endregion 

        #region IEnumerable
        /// 
        /// as IEnumerable 
        /// 
        /// this 
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
        {
            this.CheckGetEnumerator(); 
            return this;
        }
        #endregion
 
        /// 
        /// create the next object from the stream 
        ///  
        /// false if stream is finished
        public bool MoveNext() 
        {
            this.current = null;

            if (null != this.identityStack) 
            {
                this.identityStack.Clear(); 
            } 

            if (null != this.links) 
            {
                this.links.Clear();
            }
 
            bool result = false;
            if ((0 == this.version) && !this.expectingSingleValue && typeof(IEnumerable).IsAssignableFrom(this.elementType)) 
            { 
                Type implementationType = ClientType.GetImplementationType(this.elementType, typeof(ICollection<>));
                if (null != implementationType) 
                {
                    Type expectedType = implementationType.GetGenericArguments()[0]; // already know its IList<>
                    implementationType = this.elementType;
                    if (implementationType.IsInterface) 
                    {
                        implementationType = typeof(System.Collections.ObjectModel.Collection<>).MakeGenericType(expectedType); 
                    } 

                    IList list = (IList)Activator.CreateInstance(implementationType); 
                    object local = null;

                    EntityStates localState = 0;
                    while ((0 <= this.version) && 
                           this.reader.Read() &&
                           this.ReadNext(null, expectedType, AtomParseState.None, ref localState, ref local)) 
                    { 
                        list.Add(local);
                        local = null; 
                    }

                    Debug.Assert(this.version < 0, "should have completed the read");
                    this.current = list; 
                    result = true;
                } 
            } 

            if (null == this.current) 
            {
                EntityStates localState = 0;
                result = (0 <= this.version) &&
                          this.reader.Read() && 
                          this.ReadNext(null, this.elementType, AtomParseState.None, ref localState, ref this.current);
            } 
 
            if (MergeOption.NoTracking != this.MergeOptionValue)
            { 
                if (null != this.identityStack)
                {
                    foreach (KeyValuePair entity in this.identityStack)
                    { 
                        if (entity.Value.AttachToContext)
                        { 
                            this.context.AttachTo(entity.Key, entity.Value.EditLink, entity.Value.ETag, entity.Value.Entity, false); 
                        }
                    } 
                }

                if (null != this.links)
                { 
                    foreach (LinkDescriptor link in this.links)
                    { 
                        if (EntityStates.Added == link.State) 
                        {   // Added implies collection
                            if ((EntityStates.Deleted == this.context.GetEntity(link.Target).State) || 
                                (EntityStates.Deleted == this.context.GetEntity(link.Source).State))
                            {
                                this.context.DeleteLink(link.Source, link.SourceProperty, link.Target);
                            } 
                            else
                            { 
                                this.context.AttachLink(link.Source, link.SourceProperty, link.Target, this.MergeOptionValue); 
                            }
                        } 
                        else if (EntityStates.Modified == link.State)
                        {   // Modified implies reference
                            object target = link.Target;
                            if (MergeOption.PreserveChanges == this.MergeOptionValue) 
                            {
                                DataServiceContext.RelatedEnd end = this.context.GetLinks(link.Source, link.SourceProperty).FirstOrDefault(); 
                                if (null != end && null == end.TargetResouce) 
                                {   // leave the SetLink(link.Source, link.SourceProperty, null)
                                    continue; 
                                }

                                if ((null != target) && (EntityStates.Deleted == this.context.GetEntity(target).State) ||
                                    (EntityStates.Deleted == this.context.GetEntity(link.Source).State)) 
                                {
                                    target = null; 
                                } 
                            }
 
                            this.context.AttachLink(link.Source, link.SourceProperty, target, this.MergeOptionValue);
                        }
                        else
                        {   // detach link 
                            // Debug.Assert(
                            // (MergeOption.OverwriteChanges == this.MergeOptionValue) || 
                            // (MergeOption.PreserveChanges == this.MergeOptionValue && 
                            // EntityStates.Added != this.context.GetLink(link.Source, link.SourceProperty, link.Target).State),
                            // "only OverwriteChanges or PreserveChanges should remove existing links"); 
                            Debug.Assert(EntityStates.Detached == link.State, "not detached link");
                            this.context.DetachLink(link.Source, link.SourceProperty, link.Target);
                        }
                    } 
                }
            } 
 
            return result;
        } 

        /// 
        /// Not supported.
        ///  
        /// Always thrown
        void System.Collections.IEnumerator.Reset() 
        { 
            throw Error.NotSupported();
        } 

        /// set the inserted object expected in the response
        /// object being inserted that the response is looking for
        internal void SetInsertingObject(object addedObject) 
        {
            this.inserting = addedObject; 
        } 

        /// are the two values the same reference 
        /// value1
        /// value2
        /// true if they are the same reference
        private static bool AreSame(string value1, string value2) 
        {
            // bool result = Object.ReferenceEquals(value1, value2); 
            // Debug.Assert(result == (value1 == value2), "!NameTable - unable to do reference comparison on '" + value1 + "'"); 
            // XElement uses a global name table which may have encountered
            // our strings first and return a different instance than what was expected 
            bool result = (value1 == value2);
            return result;
        }
 
        /// is the reader on contentNode where the localName and namespaceUri match
        /// reader 
        /// localName 
        /// namespaceUri
        /// true if localName and namespaceUri match reader current element 
        private static bool AreSame(XmlReader reader, string localName, string namespaceUri)
        {
            Debug.Assert((null != reader) && (null != localName) && (null != namespaceUri), "null");
            return ((XmlNodeType.Element == reader.NodeType) || (XmlNodeType.EndElement == reader.NodeType)) && 
                    AreSame(reader.LocalName, localName) && AreSame(reader.NamespaceURI, namespaceUri);
        } 
 
        /// verify the GetEnumerator can only be called once
        private void CheckGetEnumerator() 
        {
            if (this.calledGetEnumerator)
            {
                throw Error.NotSupported(Strings.DataServiceException_GeneralError); 
            }
 
            this.calledGetEnumerator = true; 
        }
 
        /// 
        /// build up a single object result
        /// 
        /// what is the nested element type (collection support) 
        /// what is the expected base type for current object
        /// what is the initial parse state 
        /// entity state 
        /// current object being populated
        /// true if not at end of object 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506", Justification = "eventually need to rewrite")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "multipurpose variable")]
        private bool ReadNext(ClientType currentType, Type expectedType, AtomParseState atom, ref EntityStates entityState, ref object currentValue)
        { 
            Debug.Assert((null != this.reader) && !this.reader.EOF && (this.reader.NodeType != XmlNodeType.None), "bad XmlReader");
            this.version++; 
 
            // object level variables
            // elementType (this is our return point until the next MoveNext call) 
            string elementName = null;
            string namespaceUri = null;
            string etag = null;
            Uri identity = null; 
            Uri editLink = null;
            bool? mediaLinkEntry = null; 
            bool hasContentProperties = false; 
            XElement currentEntry = null;
            ArraySet haveSetKeyProperty; 

            // property level variables
            ClientType.ClientProperty property = null;
            string propertyName = null; 
            bool hasValue = false;
 
            ClientType nestedElementType = null; 
            object nestedValue = null;
            bool setNestedValue = false; 

#if ASTORIA_OPEN_OBJECT
            object openProperties = null; // like currentValue, this doesn't need to be nulled out
#endif 

            if ((null != currentType) && currentType.HasKeys) 
            { 
                haveSetKeyProperty = new ArraySet(currentType.KeyCount);
            } 
            else
            {
                haveSetKeyProperty = default(ArraySet);
            } 

            do 
            {   // caller of ReadNext called this.reader.Read() 
                string propertyValue = null;
                bool knownElement = false; 

                switch (this.reader.NodeType)
                {
                    #region Element 
                    case XmlNodeType.Element:
                        this.TraceElement(); 
 
                        this.CheckAgainstFailure();
 
                        elementName = this.reader.LocalName;
                        namespaceUri = this.reader.NamespaceURI;

                        if (this.expectingSingleValue) 
                        {
                            #region return known primitive/reference typed value 
                            // CreateQuery and the result is value 
                            // we don't really care what the namespace is though with astoria
                            // it would be XmlConstants.DataWebNamespace ("http://schemas.microsoft.com/ado/2007/08/dataservices") 
                            propertyValue = this.ReadElementString(true);
                            if (null != propertyValue)
                            {
                                currentValue = ClientConvert.ChangeType(propertyValue, Nullable.GetUnderlyingType(expectedType) ?? expectedType); 
                            }
 
                            this.version = -this.version; 
                            return true;
                            #endregion 
                        }

                        if (((AtomParseState.None == atom) || (AtomParseState.LinkFeed == atom)) &&
                            AreSame(XmlConstants.AtomNamespace, namespaceUri) && 
                            AreSame(XmlConstants.AtomFeedElementName, elementName))
                        { 
                            #region  
                            if (this.reader.IsEmptyElement)
                            { 
                                this.version = -this.version;
                                return false;
                            }
 
                            if (AtomParseState.None == atom)
                            {   // leave state in LinkFeed state so we exit properly when no  exist 
                                atom = AtomParseState.Feed; 
                            }
 
                            Debug.Assert(AtomParseState.Feed == atom || AtomParseState.LinkFeed == atom, "feed state");
                            knownElement = true;
                            #endregion
                        } 
                        else if (((AtomParseState.LinkEntry == atom) || (AtomParseState.LinkFeed == atom) || (AtomParseState.Feed == atom) || AtomParseState.None == atom) &&
                                 AreSame(XmlConstants.AtomNamespace, namespaceUri) && 
                                 AreSame(XmlConstants.AtomEntryElementName, elementName)) 
                        {
                            #region  
                            atom = AtomParseState.Entry;
                            this.PushBufferedReader(ref currentEntry);
                            currentType = this.ReadTypeAttribute(currentEntry, expectedType);
                            etag = this.reader.GetAttribute(XmlConstants.AtomETagAttributeName, XmlConstants.DataWebMetadataNamespace); 
                            if (currentType.HasKeys)
                            { 
                                haveSetKeyProperty = new ArraySet(currentType.KeyCount); 
                            }
 
                            knownElement = true;
                            #endregion
                        }
                        else if ((AtomParseState.Entry == atom) && 
                                 AreSame(XmlConstants.AtomNamespace, namespaceUri))
                        { 
                            if (AreSame(XmlConstants.AtomIdElementName, elementName)) 
                            {
                                #region resource uri 

                                // The "atom:id" element conveys a permanent, universally unique
                                // identifier for an entry or feed.
 
                                // Its content MUST be an IRI, as defined by [RFC3987].  Note that the
                                // definition of "IRI" excludes relative references.  Though the IRI 
                                // might use a dereferencable scheme, Atom Processors MUST NOT assume it 
                                // can be dereferenced.
                                Debug.Assert(null == currentValue, "already has an instance"); 
                                Debug.Assert(null == identity, "multiple ");

                                identity = Util.CreateUri(this.ReadElementString(false), UriKind.RelativeOrAbsolute);
                                if (!identity.IsAbsoluteUri) 
                                {
                                    throw Error.InvalidOperation(Strings.Context_TrackingExpectsAbsoluteUri); 
                                } 

                                // ReferenceIdentity is a test hook to help verify we dont' use identity instead of editLink 
                                identity = Util.ReferenceIdentity(identity);

                                knownElement = true;
                                #endregion 
                            }
                            else if (AreSame(XmlConstants.AtomContentElementName, elementName)) 
                            { 
                                #region 
                                propertyValue = this.reader.GetAttributeEx(XmlConstants.AtomContentSrcAttributeName, XmlConstants.AtomNamespace); 
                                if (propertyValue != null)
                                {
                                    // This is a media link entry
                                    // Note that in this case we don't actually use this URL (or the link/edit-media URL) 
                                    // for editing. We rely on the Astoria URL convention (/propname/$value or just /$value)
                                    if (!this.reader.IsEmptyElement) 
                                    { 
                                        throw Error.InvalidOperation(Strings.Deserialize_ExpectedEmptyMediaLinkEntryContent);
                                    } 

                                    mediaLinkEntry = true;
                                    knownElement = true;
                                } 
                                else
                                { 
                                    // This is a regular (non-media link) entry 
                                    if (mediaLinkEntry.HasValue && mediaLinkEntry.Value)
                                    { 
                                        // This means we saw a  element but now we have a Content element
                                        // that's not just a media link entry pointer (src)
                                        throw Error.InvalidOperation(Strings.Deserialize_ContentPlusPropertiesNotAllowed);
                                    } 

                                    mediaLinkEntry = false; 
 
                                    propertyValue = this.reader.GetAttributeEx(XmlConstants.AtomTypeAttributeName, XmlConstants.AtomNamespace);
                                    if (XmlConstants.MimeApplicationXml == propertyValue || XmlConstants.MimeApplicationAtom == propertyValue) 
                                    {
                                        if (this.ReadChildElement(XmlConstants.AtomPropertiesElementName, XmlConstants.DataWebMetadataNamespace))
                                        {
                                            this.TraceElement(); 
                                            hasContentProperties = true;
                                            atom = AtomParseState.Content; 
                                        } 
                                        else if (!AreSame(this.reader, XmlConstants.AtomContentElementName, XmlConstants.AtomNamespace))
                                        { 
                                            throw Error.InvalidOperation(Strings.Deserialize_NotApplicationXml);
                                        }

                                        knownElement = true; 
                                    }
                                } 
                                #endregion 
                            }
                            else if (AreSame(XmlConstants.AtomLinkElementName, elementName)) 
                            {
                                string rel = this.reader.GetAttributeEx(XmlConstants.AtomLinkRelationAttributeName, XmlConstants.AtomNamespace);
                                if (XmlConstants.AtomEditRelationAttributeValue == rel)
                                { 
                                    #region uri
                                    editLink = Util.CreateUri(this.reader.GetAttributeEx(XmlConstants.AtomHRefAttributeName, XmlConstants.AtomNamespace), UriKind.RelativeOrAbsolute); 
                                    #endregion 
                                }
                                else if (!this.reader.IsEmptyElement) 
                                {
                                    propertyName = UriUtil.GetNameFromAtomLinkRelationAttribute(rel);

                                    #region  
                                    if (null != propertyName)
                                    { 
                                        Debug.Assert(!setNestedValue, "should not be set going in link"); 

                                        property = currentType.GetProperty(propertyName, this.ignoreMissingProperties); // may throw 
                                        if (null == property)
                                        {
                                            propertyName = null;
                                        } 
                                        else
                                        { 
                                            if (property.IsKnownType) 
                                            {
                                                throw Error.InvalidOperation(Strings.Deserialize_MismatchAtomLinkLocalSimple); 
                                            }

                                            if (this.ResolveOrCreateInstance(currentType, identity, editLink, etag, ref currentValue, ref entityState))
                                            { 
                                                atom = AtomParseState.Feed;
                                                this.FireEventAndPopBufferedReader(currentValue, ref currentEntry); 
                                                return true; 
                                            }
 
                                            object parent = currentValue;
                                            Type nestedType = property.PropertyType;
                                            propertyValue = this.reader.GetAttribute(XmlConstants.AtomTypeAttributeName);
                                            if (String.Equals("application/atom+xml;type=feed", propertyValue)) 
                                            {   // parent becomes the collection, not the currentValue
                                                #region LinkFeed 
#if ASTORIA_OPEN_OBJECT 
                                                if (property.OpenObjectProperty)
                                                {   // get the nested IList from the dictionary 
                                                    nestedType = typeof(OpenObject);
                                                    ((IDictionary)property.GetValue(currentValue)).TryGetValue(propertyName, out parent);
                                                    if ((null != parent) && !(parent is IList))
                                                    { 
                                                        throw Error.InvalidOperation(Strings.Deserialize_MismatchAtomLinkFeedPropertyNotCollection(propertyName));
                                                    } 
                                                } 
                                                else
#endif 
                                                if (null == (nestedType = property.CollectionType))
                                                {
                                                    throw Error.InvalidOperation(Strings.Deserialize_MismatchAtomLinkFeedPropertyNotCollection(propertyName));
                                                } 
                                                else
                                                {   // get the nested IList from the current value 
                                                    parent = property.GetValue(currentValue); 
                                                }
 
                                                if (null == parent)
                                                {
                                                    setNestedValue = true;
 
                                                    // if the current property is not settable
                                                    // we fail after populating contents into a collection 
                                                    Type implementationType; 
#if ASTORIA_OPEN_OBJECT
                                                    if (property.OpenObjectProperty) 
                                                    {
                                                        implementationType = typeof(System.Collections.ObjectModel.Collection);
                                                    }
                                                    else 
#endif
                                                    { 
                                                        implementationType = property.PropertyType; 
                                                        if (implementationType.IsInterface)
                                                        { 
                                                            implementationType = typeof(System.Collections.ObjectModel.Collection<>).MakeGenericType(nestedType);
                                                        }
                                                    }
 
                                                    parent = Activator.CreateInstance(implementationType);
                                                } 
 
                                                atom = AtomParseState.LinkFeed;
                                                #endregion 
                                            }
                                            else if (String.Equals("application/atom+xml;type=entry", propertyValue))
                                            {
                                                #region LinkEntry 
#if ASTORIA_OPEN_OBJECT
                                                if (property.OpenObjectProperty) 
                                                { 
                                                    nestedType = typeof(OpenObject);
                                                } 
                                                else
#endif
                                                if (null != property.CollectionType)
                                                { 
                                                    throw Error.InvalidOperation(Strings.Deserialize_MismatchAtomLinkEntryPropertyIsCollection(propertyName));
                                                } 
 
                                                atom = AtomParseState.LinkEntry;
                                                #endregion 
                                            }

                                            int linkCount = 0;
                                            AtomParseState linkStart = atom; 

                                            #region read  
                                            // Check for empty inline element - that indicates that the value is null 
                                            // This is only true for reference properties
                                            if (this.ReadChildElement(XmlConstants.AtomInlineElementName, XmlConstants.DataWebMetadataNamespace)) 
                                            {
                                                this.TraceElement();

                                                Debug.Assert( 
                                                    (currentValue == parent && AtomParseState.LinkEntry == atom) ||
                                                    (currentValue != parent && AtomParseState.LinkFeed == atom && parent is IEnumerable), 
                                                    "unexpected parent"); 

                                                Debug.Assert(null == nestedValue, "nestedValue should be null before reading links"); 
                                                Debug.Assert(AtomParseState.LinkFeed == atom || AtomParseState.LinkEntry == atom, "expecting LinkEntry or LinkFeed");

                                                if (!this.reader.IsEmptyElement)
                                                { 
                                                    #region read 
                                                    EntityStates nestedState = 0; 
                                                    while ((0 <= this.version) && 
                                                           this.reader.Read() &&
                                                           this.ReadNext(null, nestedType, atom, ref nestedState, ref nestedValue)) 
                                                    {
                                                        // will currentValue.Collection.Add or currentValue.set_Reference
                                                        if ((MergeOption.AppendOnly != this.MergeOptionValue) ||
                                                            (EntityStates.Detached == entityState)) 
                                                        {
#if ASTORIA_OPEN_OBJECT 
                                                            if ((setNestedValue || parent is IList) && property.OpenObjectProperty) 
                                                            {
                                                                ((IList)parent).Add((OpenObject)nestedValue); 
                                                            }
                                                            else
                                                            {
                                                                property.SetValue(parent, nestedValue, propertyName, ref openProperties, true); 
                                                            }
#else 
                                                            property.SetValue(parent, nestedValue, propertyName, true); 
#endif
 
                                                            // if null == nestedValue, that implies an existing reference may now be gone
                                                            // however, since we don't "refresh" - we leave that previous relationship intact
                                                            if ((null != nestedValue) &&
#if ASTORIA_OPEN_OBJECT 
                                                                 !property.OpenObjectProperty &&
#endif 
 currentType.HasKeys && ClientType.Create(nestedValue.GetType()).HasKeys) 
                                                            {
                                                                if (null == this.links) 
                                                                {
                                                                    this.links = new List();
                                                                }
 
                                                                // cache the links we want to add - but can't until after both ends exist in the context
                                                                EntityStates linkState = (AtomParseState.LinkEntry == linkStart) ? EntityStates.Modified : EntityStates.Added; 
                                                                this.links.Add(new LinkDescriptor(currentValue, property.PropertyName, nestedValue, linkState)); 
                                                            }
                                                        } 

                                                        linkCount++;
                                                        nestedValue = null;
                                                        atom = AtomParseState.LinkEntry; 

                                                        if ((AtomParseState.LinkEntry == linkStart) && (0 != linkCount)) 
                                                        {   //  
                                                            break; // while
                                                        } 
                                                    }
                                                    #endregion
                                                }
 
                                                #region no links
                                                if ((0 == linkCount) && (AtomParseState.LinkEntry == linkStart)) 
                                                {   // set null nested value 
                                                    if ((MergeOption.AppendOnly != this.MergeOptionValue) ||
                                                        (EntityStates.Detached == entityState)) 
                                                    {
                                                        if (null == this.links)
                                                        {
                                                            this.links = new List(); 
                                                        }
 
                                                        // AppendOnly & NoTracking - only if creating new object 
                                                        // OverwriteChanges - server wins
                                                        // PreserveChanges - new object, non-null target -> unchanged, null target -> modified 
                                                        Debug.Assert(
                                                            (MergeOption.AppendOnly == this.MergeOptionValue && EntityStates.Detached == entityState) ||
                                                            (MergeOption.NoTracking == this.MergeOptionValue && EntityStates.Detached == entityState) ||
                                                            (MergeOption.OverwriteChanges == this.MergeOptionValue) || 
                                                            (MergeOption.PreserveChanges == this.MergeOptionValue),
                                                            "unexpected merge of link reference"); 
 
                                                        Debug.Assert(null == nestedValue, "expecting null nestedValue");
                                                        this.links.Add(new LinkDescriptor(currentValue, property.PropertyName, null, EntityStates.Modified)); 
                                                        parent = null;
                                                        setNestedValue = true;
                                                    }
                                                } 
                                                else if ((AtomParseState.LinkFeed == linkStart) &&
                                                    (MergeOption.OverwriteChanges == this.MergeOptionValue || MergeOption.PreserveChanges == this.MergeOptionValue)) 
                                                {   // detach extra remaining links 
                                                    object collection = null;
                                                    var q = (from x in this.context.GetLinks(currentValue, property.PropertyName) 
                                                             where MergeOption.OverwriteChanges == this.MergeOptionValue || EntityStates.Added != x.State
                                                             select new LinkDescriptor(x.SourceResource, x.SourceProperty, x.TargetResouce, EntityStates.Detached))
                                                            .Except(this.links, LinkDescriptor.EquivalentComparer);
 
                                                    foreach (LinkDescriptor p in q)
                                                    { 
                                                        if (null == collection) 
                                                        {
                                                            collection = property.GetValue(currentValue); 
                                                        }

                                                        if (null != collection)
                                                        { 
                                                            property.RemoveValue(collection, p.Target);
                                                        } 
 
                                                        this.links.Add(p);
                                                    } 
                                                }
                                                #endregion

                                                if (setNestedValue) 
                                                {
                                                    nestedValue = parent; 
                                                } 

                                                Debug.Assert(this.reader.IsEmptyElement || XmlNodeType.EndElement == this.reader.NodeType, "expected EndElement"); 
                                                Debug.Assert(
                                                    (this.reader.IsEmptyElement && AreSame(XmlConstants.AtomInlineElementName, this.reader.LocalName)) ||
                                                    (AtomParseState.LinkFeed == linkStart && AreSame(XmlConstants.AtomFeedElementName, this.reader.LocalName)) ||
                                                    (AtomParseState.LinkEntry == linkStart && AreSame(XmlConstants.AtomEntryElementName, this.reader.LocalName)), 
                                                    "link didn't end on expected element");
                                            } 
 
                                            this.SkipToEnd(XmlConstants.AtomLinkElementName, XmlConstants.AtomNamespace);
                                            #endregion 

                                            Debug.Assert(((XmlNodeType.Element == this.reader.NodeType && this.reader.IsEmptyElement) ||
                                                          (XmlNodeType.EndElement == this.reader.NodeType)) &&
                                                          AreSame(this.reader, XmlConstants.AtomLinkElementName, XmlConstants.AtomNamespace), 
                                                         "didn't land on ");
 
                                            #region Set collection / reference instance 

                                            if (setNestedValue) 
                                            {
#if ASTORIA_OPEN_OBJECT
                                                property.SetValue(currentValue, nestedValue, propertyName, ref openProperties, false);
#else 
                                                property.SetValue(currentValue, nestedValue, propertyName, false);
#endif 
                                                nestedValue = null; 
                                            }
                                            #endregion 

                                            setNestedValue = false;
                                            propertyName = null;
                                            property = null; 
                                            Debug.Assert(null == nestedValue, "null == nestedValue");
                                            knownElement = true; 
                                        } 
                                    }
 
                                    atom = AtomParseState.Entry;
                                    #endregion
                                }
                            } 
                        }
                        else if ((AtomParseState.Entry == atom) && 
                                 AreSame(XmlConstants.DataWebMetadataNamespace, namespaceUri) && 
                                 AreSame(XmlConstants.AtomPropertiesElementName, elementName))
                        { 
                            #region 
                            if (mediaLinkEntry.HasValue && !mediaLinkEntry.Value)
                            {
                                // This means we saw a non-empty  element but now we have a Properties element 
                                // that also carries properties
                                throw Error.InvalidOperation(Strings.Deserialize_ContentPlusPropertiesNotAllowed); 
                            } 

                            mediaLinkEntry = true; 

                            if (!this.reader.IsEmptyElement)
                            {
                                atom = AtomParseState.Content; // Semantically this is equivalent to content, so treat it the same 
                            }
 
                            knownElement = true; 
                            #endregion
                        } 
                        else if (((AtomParseState.Content == atom) || (AtomParseState.Complex == atom)) &&
                                 AreSame(this.dataNamespace, namespaceUri))
                        {
                            if (this.ResolveOrCreateInstance(currentType, identity, editLink, etag, ref currentValue, ref entityState)) 
                            {
                                atom = AtomParseState.Feed; 
                                this.FireEventAndPopBufferedReader(currentValue, ref currentEntry); 
                                return true;
                            } 

                            #region MergeOption checks
                            Debug.Assert(null != currentValue, "null currentValue");
                            Debug.Assert(0 != entityState, "expected entitystate"); 

                            if (((MergeOption.AppendOnly == this.MergeOptionValue) && (EntityStates.Detached != entityState)) || 
                                ((MergeOption.PreserveChanges == this.MergeOptionValue) && (EntityStates.Added == entityState || EntityStates.Modified == entityState))) 
                            {   // do not change content in this state
                                this.SkipToEnd(this.reader.LocalName, this.reader.NamespaceURI); 
                                break;
                            }

                            Debug.Assert( 
                                (MergeOption.NoTracking == this.MergeOptionValue && EntityStates.Detached == entityState) ||
                                (MergeOption.AppendOnly == this.MergeOptionValue && EntityStates.Detached == entityState) || 
                                (MergeOption.PreserveChanges == this.MergeOptionValue && (EntityStates.Detached == entityState || EntityStates.Unchanged == entityState || EntityStates.Deleted == entityState)) || 
                                (MergeOption.OverwriteChanges == this.MergeOptionValue),
                                "NoTracking and AppendOnly, PreserveChanges should not change content of existing objects"); 
                            #endregion

                            #region 
                            property = currentType.GetProperty(elementName, this.ignoreMissingProperties); // may throw 
                            if (null != property)
                            { 
                                propertyName = elementName; 
                                atom = (AtomParseState.Content == atom) ? AtomParseState.PropertyContent : AtomParseState.PropertyComplex;
 
                                if (property.KeyProperty)
                                {
                                    haveSetKeyProperty.Add(propertyName, null);
                                } 

#if ASTORIA_OPEN_OBJECT 
                                if (property.IsKnownType || 
                                    ClientConvert.IsKnownType((nestedElementType = this.ReadTypeAttribute(property.OpenObjectProperty ? typeof(OpenObject) : property.PropertyType)).ElementType))
#else 
                                if (property.IsKnownType ||
                                    ClientConvert.IsKnownType((nestedElementType = this.ReadTypeAttribute(property.PropertyType)).ElementType))
#endif
                                { 
                                    // propertyValue
                                    propertyValue = this.ReadElementString(true); 
 
                                    object value = propertyValue;
                                    if (null != propertyValue) 
                                    {
                                        value = ClientConvert.ChangeType(propertyValue, (null != nestedElementType ? nestedElementType.ElementType : property.PropertyType));
                                    }
 
#if ASTORIA_OPEN_OBJECT
                                    property.SetValue(currentValue, value, propertyName, ref openProperties, false); 
#else 
                                    property.SetValue(currentValue, value, propertyName, false);
#endif 

                                    atom = (AtomParseState.PropertyContent == atom) ? AtomParseState.Content : AtomParseState.Complex;
                                    property = null;
                                    propertyName = null; 
                                    nestedElementType = null;
                                    hasValue = false; 
                                } 
                                else if (this.reader.IsEmptyElement)
                                { 
#if ASTORIA_OPEN_OBJECT
                                    property.SetValue(currentValue, null, propertyName, ref openProperties, false);
#else
                                    property.SetValue(currentValue, null, propertyName, false); 
#endif
                                } 
                                else 
                                {
#if ASTORIA_OPEN_OBJECT 
                                    if (!property.OpenObjectProperty && (null != property.CollectionType))
#else
                                    if (null != property.CollectionType)
#endif 
                                    {   // client object property is collection implying nested collection of complex objects
                                        throw Error.NotSupported(Strings.ClientType_CollectionOfNonEntities); 
                                    } 

                                    #region recurse into complex type 
                                    if (null == nestedValue)
                                    {   // retreived only once per collection or nested object
                                        nestedValue = property.GetValue(currentValue);
 
#if ASTORIA_OPEN_OBJECT
                                        if (property.OpenObjectProperty) 
                                        { 
                                            if (null != nestedValue)
                                            { 
                                                ((IDictionary)nestedValue).TryGetValue(propertyName, out nestedValue);
                                            }
                                            else
                                            { 
                                                throw Error.InvalidOperation(Strings.Collection_NullCollectionReference(currentType.ElementTypeName, property.PropertyName));
                                            } 
                                        } 
#endif
                                    } 

                                    if (null == nestedValue)
                                    {   // when the value is null, assume the recursion will create the object
                                        hasValue = true; 
                                    }
 
                                    if (this.reader.Read() && 
                                        this.ReadNext(nestedElementType, nestedElementType.ElementType, AtomParseState.Complex, ref entityState, ref nestedValue))
                                    { 
                                        setNestedValue = (hasValue = (nestedValue != null));
                                        hasValue = true;
                                        goto case XmlNodeType.EndElement;
                                    } 
                                    #endregion
                                } 
 
                                knownElement = true;
                            } 
                            #endregion
                        }
                        else if ((AtomParseState.None == atom) && AreSame(this.dataNamespace, namespaceUri) && (null == currentType))
                        {   // complex type? 
                            Debug.Assert(0 == entityState, "shouldn't have existing entity");
                            ClientType tmp = this.ReadTypeAttribute(expectedType); 
                            if (!tmp.HasKeys) 
                            {
                                currentType = tmp; 
                                if (this.reader.IsEmptyElement || this.DoesNullAttributeSayTrue())
                                {
                                    this.SkipToEnd(this.reader.LocalName, this.reader.NamespaceURI);
                                    return true; 
                                }
 
                                atom = AtomParseState.Complex; 
                                entityState = EntityStates.Detached;
                                knownElement = true; 
                            }
                        }

                        if (!knownElement) 
                        {
                            this.SkipToEnd(this.reader.LocalName, this.reader.NamespaceURI); 
                        } 

                        break; 

                    #endregion

                    #region Text 
                    case XmlNodeType.Text:
                    case XmlNodeType.SignificantWhitespace: 
                        Debug.Assert(!this.expectingSingleValue, "!this.expectingSingleValue, Text"); 

                        // throw an exception that we hit mixed-content 
                        // propertyValueelementText
                        throw Error.InvalidOperation(Strings.Deserialize_MixedContent(currentType.ElementTypeName));
                    #endregion
 
                    #region EndElement
                    case XmlNodeType.EndElement: 
                        Debug.Assert(!this.expectingSingleValue, "!this.expectingSingleValue"); 
                        this.TraceEndElement(true);
 
                        if (null == currentType)
                        {   // 
                            // or reader started in a bad state (which is okay)
                            Debug.Assert(!hasValue, "EndElement w/ haveValue"); 
                            Debug.Assert(!setNestedValue, "EndElement w/ setNestedValue");
                            Debug.Assert(null == property, "EndElement w/ property"); 
                            Debug.Assert(null == propertyName, "EndElement w/ propertyName"); 
                            Debug.Assert(null == nestedValue, "EndElement w/ nestedValue");
                            Debug.Assert(null == nestedElementType, "EndElement w/ nestedElementType"); 

                            // SQLBUDT 565573
                            if (AtomParseState.LinkEntry != atom && AtomParseState.LinkFeed != atom)
                            { 
                                atom = AtomParseState.None;
                                this.version = -this.version; 
                            } 

                            return false; 
                        }

                        if (null == property)
                        {   //  
                            Debug.Assert(!hasValue, "EndElement property w/ haveValue");
                            Debug.Assert(!setNestedValue, "EndElement property w/ setNestedValue"); 
                            Debug.Assert(null == property, "EndElement property w/ propertyName"); 
                            Debug.Assert(null == nestedValue, "EndElement property w/ nestedValue");
                            Debug.Assert(null == nestedElementType, "EndElement property w/ nestedElementType"); 
                            Debug.Assert(null != currentType, "Should have derived a type from the entry or from the query");

                            if (AtomParseState.Content == atom)
                            { 
                                if (hasContentProperties)
                                { 
                                    this.SkipToEnd(XmlConstants.AtomContentElementName, XmlConstants.AtomNamespace); 
                                }
 
                                atom = AtomParseState.Entry;
                                continue;
                            }
                            else if (AtomParseState.Complex == atom) 
                            {
                                return true; 
                            } 
                            else if (AtomParseState.Entry == atom)
                            { 
                                this.SkipToEnd(XmlConstants.AtomEntryElementName, XmlConstants.AtomNamespace);
                                atom = AtomParseState.Feed;

                                // it might happen that we see no data properties in an entry, just 
                                // metadata such as the ID. In the case were no properties where present
                                // we just create an uninitialized entry and attach it if needed 
                                this.ResolveOrCreateInstance(currentType, identity, editLink, etag, ref currentValue, ref entityState); 

                                // validate all keys have been set 
                                if (currentType.HasKeys && (EntityStates.Added == entityState || EntityStates.Detached == entityState))
                                {
                                    foreach (ClientType.ClientProperty p in currentType.Properties)
                                    { 
                                        // if property is a key and not set
                                        if (p.KeyProperty && (null == haveSetKeyProperty.Remove(p.PropertyName, String.Equals))) 
                                        { 
                                            throw Error.InvalidOperation(Strings.BatchStream_ContentExpected(p.PropertyName));
                                        } 
                                    }
                                }

                                this.FireEventAndPopBufferedReader(currentValue, ref currentEntry); 
                                return true;
                            } 
 
                            Debug.Assert(false, "invalid Atom state");
                            this.version = -this.version; 
                            Error.ThrowInternalError(InternalError.UnexpectedReadState);
                        }

                        //  
                        Debug.Assert(null != currentValue, "null currentValue");
                        Debug.Assert(null != propertyName, "null propertyName"); 
                        Debug.Assert(!setNestedValue || (null != property), "null property w/ nestedValue"); 

                        if ((null != nestedValue) && 
                            (setNestedValue ||
#if ASTORIA_OPEN_OBJECT
                              property.OpenObjectProperty ||
#endif 
 nestedValue.GetType().IsValueType))
                        { 
                            Debug.Assert(null != nestedValue, "EndElement after nested w/o nestedValue"); 

                            // if hasValue is false, we still want to set the nestedValue instance 
                            // 

                            // which is different than 
                            // which would have set null for nested complex property 
#if ASTORIA_OPEN_OBJECT
                            property.SetValue(currentValue, nestedValue, propertyName, ref openProperties, false); 
#else 
                            property.SetValue(currentValue, nestedValue, propertyName, false);
#endif 
                        }
                        else if (!hasValue)
                        {   // 
                            Debug.Assert(null != property, "null property without value"); 
#if ASTORIA_OPEN_OBJECT
                            property.SetValue(currentValue, null, propertyName, ref openProperties, false); 
#else 
                            property.SetValue(currentValue, null, propertyName, false);
#endif 
                        }

                        // finished reading property, continue parsing element
                        Debug.Assert( 
                            AtomParseState.PropertyContent == atom ||
                            AtomParseState.PropertyComplex == atom, 
                            "expected atom property state"); 
                        atom = (AtomParseState.PropertyContent == atom) ? AtomParseState.Content : AtomParseState.Complex;
                        property = null; 
                        propertyName = null;
                        hasValue = false;

                        nestedElementType = null; 
                        nestedValue = null;
                        setNestedValue = false; 
                        break; 
                    #endregion
 
                    #region fail on unhandled content nodes, similar to XmlReader.MoveToContent()
                    case XmlNodeType.CDATA:
                    case XmlNodeType.EntityReference:
                    case XmlNodeType.EndEntity: 
                        // Checks whether the current node is a content (non-whitespace text, CDATA, Element, EndElement, EntityReference
                        // or EndEntity) node. If the node is not a content node, then the method skips ahead to the next content node or 
                        // end of file. Skips over nodes of type ProcessingInstruction, DocumentType, Comment, Whitespace and SignificantWhitespace. 
                        this.version = -this.version;
                        Error.ThrowInternalError(InternalError.UnexpectedXmlNodeTypeWhenReading); 
                        break;

                    #endregion
                } 
            }
            while (this.reader.Read()); 
 
            Debug.Assert(null == currentValue, "non-null currentValue");
            this.version = -this.version; 
            return false;
        }

        ///  
        /// create an instance and associate its identity for callstack identity resolution
        ///  
        /// type to create 
        /// identity of instance
        /// editLink of instance 
        /// etag of identity
        /// current value instance
        /// entity state
        /// new instance of type 
        private bool ResolveOrCreateInstance(ClientType type, Uri identity, Uri editLink, string etag, ref object currentValue, ref EntityStates state)
        { 
            if (null == currentValue) 
            {
                if (null != identity) 
                {
                    #region handle POST entity response
                    if (null != this.inserting)
                    { 
                        Debug.Assert(MergeOption.OverwriteChanges == this.MergeOptionValue, "only expected value during SaveChanges");
 
                        // always attach an identity, so that inserted relationships can also happen 
                        // however, if NoTracking then its left in the inserted bucket
                        this.context.AttachIdentity(identity, editLink, this.inserting, etag); 
                    }
                    #endregion

                    if (type.HasKeys) 
                    {
                        Debug.Assert(null != identity, "missing identity"); 
                        if (MergeOption.NoTracking != this.MergeOptionValue) 
                        {
                            #region handle POST entity response 
                            if (null != this.inserting)
                            {
                                Debug.Assert(null == currentValue, "it already has a value?");
                                currentValue = this.inserting; 
                                state = EntityStates.Unchanged;
                                this.inserting = null; 
 
                                Debug.Assert(type.HasKeys, "!HasKeys");
                                Debug.Assert(type.ElementType.IsInstanceOfType(currentValue), "!IsInstanceOfType"); 
                            }
                            #endregion

                            if (null != (currentValue ?? (currentValue = this.context.TryGetEntity(identity, etag, this.MergeOptionValue, out state)))) 
                            {
                                if (!type.ElementType.IsInstanceOfType(currentValue)) 
                                { 
                                    throw Error.InvalidOperation(Strings.Deserialize_Current(type.ElementType, currentValue.GetType()));
                                } 
                            }
                        }
                    }
 
                    if ((null == currentValue) && (null != this.identityStack))
                    {   // always do identity resolution on the callstack within a top-level entity 
                        // but avoid the AppendOnly behavior for deep expanded items 
                        EntityWithTag pair;
                        this.identityStack.TryGetValue(identity, out pair); 
                        currentValue = pair.Entity;
                        state = pair.State;
                    }
                } 
                else
                { 
                    Debug.Assert(!type.HasKeys, "entity without identity"); 
                }
 
                if ((null == currentValue) || (this.MergeOptionValue == MergeOption.OverwriteChanges))
                {
                    if (null == currentValue)
                    { 
                        currentValue = type.CreateInstance();
                        state = EntityStates.Detached; 
                    } 

                    if (null != identity) 
                    {
                        if (null == this.identityStack)
                        {
                            this.identityStack = new Dictionary(); 
                        }
 
                        this.identityStack.Add(identity, new EntityWithTag(currentValue, editLink, etag, state, type.HasKeys)); 
                    }
                } 
            }

            return false;
        } 

        ///  
        /// Read subtree into an XElement, push the reader into the stack and set the current 
        /// reader to a reader on top of the XElement
        ///  
        /// Current XElement being processed
        private void PushBufferedReader(ref XElement currentEntry)
        {
            XmlReader subtree = this.reader.ReadSubtree(); 
            try
            { 
                currentEntry = XElement.Load(subtree); 
            }
            catch (XmlException ex) 
            {
                throw new DataServiceClientException(Strings.Deserialize_ServerException1, ex);
            }
            finally 
            {
                subtree.Close(); 
            } 

            if (this.stackedReaders == null) 
            {
                this.stackedReaders = new Stack();
            }
 
            this.stackedReaders.Push(this.reader);
            this.reader = currentEntry.CreateReader(); 
            this.reader.Read(); 
        }
 
        /// 
        /// Fire the reader event with the current buffered subtree reader and then pop
        /// the next reader in the stack
        ///  
        /// Current .NET object being read
        /// Buffered entry with data from the input stream 
        private void FireEventAndPopBufferedReader(object currentValue, ref XElement currentEntry) 
        {
            if (this.context.HasReadingEntityHandlers) 
            {
                Debug.Assert(currentEntry != null && currentValue != null, "Event present but did not create buffered entry?");
                Debug.Assert(this.stackedReaders.Count > 0, "Event present but previous reader not pushed?");
 
                this.context.FireReadingEntityEvent(currentValue, currentEntry);
            } 
 
            this.reader = this.stackedReaders.Pop();
            currentEntry = null; 
        }

        /// 
        /// Gets the type attribte and resolves the type from the current XmlReader. 
        /// 
        /// expected base type 
        /// resolved type 
        private ClientType ReadTypeAttribute(Type expectedType)
        { 
            string typeName = this.reader.GetAttribute(XmlConstants.AtomTypeAttributeName, XmlConstants.DataWebMetadataNamespace);
            Type resolvedType = this.context.ResolveTypeFromName(typeName, expectedType);
            return ClientType.Create(resolvedType);
        } 

        ///  
        /// Gets the type attribte and resolves the type from the specified . 
        /// 
        /// XML element for the ATOM entry. 
        /// expected base type
        /// resolved type
        private ClientType ReadTypeAttribute(XElement entryElement, Type expectedType)
        { 
            Debug.Assert(entryElement != null, "entryElement != null");
 
            // XNamespace.None is used for attribute because they are not namespace-qualified in the payloads. 
            XNamespace atomNs = XmlConstants.AtomNamespace;
            XName categoryName = atomNs + XmlConstants.AtomCategoryElementName; 
            XName schemeName = XNamespace.None + XmlConstants.AtomCategorySchemeAttributeName;
            XName termName = XNamespace.None + XmlConstants.AtomCategoryTermAttributeName;
            string typeSchemeText = this.context.TypeScheme.OriginalString;
 
            var categories = entryElement.Elements(categoryName);
            var category = categories.Where(c => c.Attributes(schemeName).Any(a => a.Value == typeSchemeText)).FirstOrDefault(); 
            var categoryTitle = (category == null) ? null : category.Attributes(termName).FirstOrDefault(); 

            string typeName = (categoryTitle == null) ? null : categoryTitle.Value; 
            Type resolvedType = this.context.ResolveTypeFromName(typeName, expectedType);
            return ClientType.Create(resolvedType);
        }
 
        /// 
        /// it can concatenate multiple adjacent text and CDATA blocks 
        /// ignores comments and whitespace 
        /// will leave reader on EndElement
        ///  
        /// check for null attribute or not
        /// text contained in the element. An null string if the element was empty
        private string ReadElementString(bool checkNullAttribute)
        { 
            Debug.Assert(XmlNodeType.Element == this.reader.NodeType, "not positioned on Element");
 
            string result = null; 
            bool empty = checkNullAttribute && !this.DoesNullAttributeSayTrue();
 
            if (this.reader.IsEmptyElement)
            {
                return (empty ? String.Empty : null);
            } 

            while (this.reader.Read()) 
            { 
                switch (this.reader.NodeType)
                { 
                    case XmlNodeType.EndElement:
                        this.TraceEndElement(false);
                        return result ?? (empty ? String.Empty : null);
                    case XmlNodeType.CDATA: 
                    case XmlNodeType.Text:
                    case XmlNodeType.SignificantWhitespace: 
                        if (null != result) 
                        {
                            throw Error.InvalidOperation(Strings.Deserialize_MixedTextWithComment); 
                        }

                        this.TraceText(this.reader.Value);
                        result = this.reader.Value; 
                        break;
                    case XmlNodeType.Comment: 
                    case XmlNodeType.Whitespace: 
                        break;
 
                    #region XmlNodeType error
                    case XmlNodeType.Element:
                        this.CheckAgainstFailure();
                        goto default; 

                    default: 
                        this.version = -this.version; 
                        throw Error.InvalidOperation(Strings.Deserialize_ExpectingSimpleValue);
                    #endregion 
                }
            }

            // xml ended before EndElement? 
            this.version = -this.version;
            throw Error.InvalidOperation(Strings.Deserialize_ExpectingSimpleValue); 
        } 

        /// Move to top-level child element of the expected type 
        /// localName of expected child content-node
        /// namespaceUri of expected child content-node
        /// true if moved to expected child content node
        private bool ReadChildElement(string localName, string namespaceUri) 
        {
            return (!this.reader.IsEmptyElement && 
                    (this.reader.NodeType != XmlNodeType.EndElement) && 
                    this.reader.Read() &&
                    this.reader.IsStartElement(localName, namespaceUri)); 
        }

        /// 
        /// Skips the children of the current node. 
        /// 
        /// local name of the node we search to 
        /// namespace of the node we search to 
        private void SkipToEnd(string localName, string namespaceURI)
        { 
            // 
            // 
            // 
            XmlReader xmlReader = this.reader; 
            int readerDepth = xmlReader.Depth;
 
            do 
            {
                switch (xmlReader.NodeType) 
                {
                    case XmlNodeType.Element:
                        this.CheckAgainstFailure();
                        if (xmlReader.IsEmptyElement && 
                            xmlReader.Depth == readerDepth &&
                            AreSame(xmlReader, localName, namespaceURI)) 
                        { 
                            return;
                        } 

                        break;

                    case XmlNodeType.EndElement: 
                        if (xmlReader.Depth <= readerDepth)
                        {   // new end element 
                            this.TraceEndElement(true); 
                            readerDepth--;
 
                            if (AreSame(xmlReader, localName, namespaceURI))
                            {
                                return;
                            } 
                        }
 
                        break; 
                }
            } 
            while (xmlReader.Read());
        }

        /// throw if current element is Exception 
        /// if current element is exception
        private void CheckAgainstFailure() 
        { 
            Debug.Assert(XmlNodeType.Element == this.reader.NodeType, "not Element");
            if (AreSame(this.reader, XmlConstants.XmlErrorElementName, XmlConstants.DataWebMetadataNamespace)) 
            {
                string message = this.ReadErrorMessage();
                this.version = -this.version;
 
                // In case of instream errors, the status code should be 500 (which is the default)
                throw new DataServiceClientException(Strings.Deserialize_ServerException(message)); 
            } 
        }
 
        /// With the reader positioned on an 'error' element, reads the text of the 'message' child.
        /// The text of the 'message' child element, empty if not found.
        private string ReadErrorMessage()
        { 
            Debug.Assert(AreSame(this.reader, XmlConstants.XmlErrorElementName, XmlConstants.DataWebMetadataNamespace), "AreSame(this.reader, XmlConstants.XmlErrorElementName, XmlConstants.DataWebMetadataNamespace)");
            int depth = 1; 
            while (depth > 0 && this.reader.Read()) 
            {
                if (this.reader.NodeType == XmlNodeType.Element) 
                {
                    if (!this.reader.IsEmptyElement)
                    {
                        depth++; 
                    }
 
                    if (depth == 2 && 
                        AreSame(this.reader, XmlConstants.XmlErrorMessageElementName, XmlConstants.DataWebMetadataNamespace))
                    { 
                        return this.ReadElementString(false);
                    }
                }
                else if (this.reader.NodeType == XmlNodeType.EndElement) 
                {
                    depth--; 
                } 
            }
 
            return String.Empty;
        }

        ///  
        /// check the atom:null="true" attribute
        ///  
        /// true of null is true 
        private bool DoesNullAttributeSayTrue()
        { 
            string attributeValue = this.reader.GetAttribute(XmlConstants.AtomNullAttributeName, XmlConstants.DataWebMetadataNamespace);
            return ((null != attributeValue) && XmlConvert.ToBoolean(attributeValue));
        }
 
        #region Tracing
 
        ///  
        /// trace Element node
        ///  
        [Conditional("TRACE")]
        private void TraceElement()
        {
            Debug.Assert(XmlNodeType.Element == this.reader.NodeType, "not positioned on Element"); 

            if (null != this.writer) 
            { 
                this.writer.Write(Util.GetWhitespaceForTracing(2 + this.reader.Depth), 0, 2 + this.reader.Depth);
                this.writer.Write("<{0}", this.reader.Name); 

                if (this.reader.MoveToFirstAttribute())
                {
                    do 
                    {
                        this.writer.Write(" {0}=\"{1}\"", this.reader.Name, this.reader.Value); 
                    } 
                    while (this.reader.MoveToNextAttribute());
 
                    this.reader.MoveToElement();
                }

                this.writer.Write(this.reader.IsEmptyElement ? " />" : ">"); 
            }
        } 
 
        /// 
        /// trace EndElement node 
        /// 
        /// indent or not
        [Conditional("TRACE")]
        private void TraceEndElement(bool indent) 
        {
            if (null != this.writer) 
            { 
                if (indent)
                { 
                    this.writer.Write(Util.GetWhitespaceForTracing(2 + this.reader.Depth), 0, 2 + this.reader.Depth);
                }

                this.writer.Write("", this.reader.Name); 
            }
        } 
 
        /// 
        /// trace string value 
        /// 
        /// value
        [Conditional("TRACE")]
        private void TraceText(string value) 
        {
            if (null != this.writer) 
            { 
                this.writer.Write(value);
            } 
        }
        #endregion

        ///  
        /// hold information about a newly constructed object until the very end of MoveNext
        ///  
        private struct EntityWithTag 
        {
            /// entity being attached 
            internal readonly object Entity;

            /// uri to edit the entity
            /// <atom:link rel="edit" href="editLink" /> 
            internal readonly Uri EditLink;
 
            /// etag of entity being attached 
            internal readonly string ETag;
 
            /// state of the entitiy
            internal readonly EntityStates State;

            /// should we actually attach the entity, i.e. false if OpenType 
            internal readonly bool AttachToContext;
 
            ///  
            /// ctor
            ///  
            /// entity
            /// editLink
            /// etag
            /// state 
            /// attach
            internal EntityWithTag(object entity, Uri editLink, string etag, EntityStates state, bool attach) 
            { 
                this.Entity = entity;
                this.EditLink = editLink; 
                this.ETag = etag;
                this.State = state;
                this.AttachToContext = attach;
            } 
        }
 
        ///  
        /// materialize objects from an application/atom+xml stream
        ///  
        /// base type of the object to create from the stream
        internal sealed class MaterializeAtomT : MaterializeAtom, IEnumerable, IEnumerator
        {
            ///  
            /// constructor
            ///  
            /// originating context 
            /// reader
            internal MaterializeAtomT(DataServiceContext context, XmlReader reader) 
                : base(context, reader, typeof(T), context.MergeOption)
            {
            }
 
            #region Current
            ///  
            /// typed current object 
            /// 
            [DebuggerHidden] 
            T IEnumerator.Current
            {
                get { return (T)this.Current; }
            } 

            #endregion 
 
            #region IEnumerable
            ///  
            /// as IEnumerable
            /// 
            /// this
            IEnumerator IEnumerable.GetEnumerator() 
            {
                this.CheckGetEnumerator(); 
                return this; 
            }
 
            #endregion
        }
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
// materialize objects from an xml stream
//  
//--------------------------------------------------------------------- 

namespace System.Data.Services.Client 
{
    using System;
    using System.Collections;
    using System.Collections.Generic; 
    using System.Diagnostics;
    using System.Linq; 
    using System.Xml; 
    using System.Xml.Linq;
 
    /// 
    /// materialize objects from an application/atom+xml stream
    /// 
    internal class MaterializeAtom : IDisposable, IEnumerable, IEnumerator 
    {
        /// MergeOption captured from context so its static for the entire materialization 
        internal readonly MergeOption MergeOptionValue; 

        /// Options when deserializing properties to the target type. 
        private readonly bool ignoreMissingProperties;

        /// Backreference to the context to allow type resolution.
        private readonly DataServiceContext context; 

        /// DataNamespace captured from context so its static for the entire materialization 
        /// reference equality is important here 
        private readonly string dataNamespace;
 
        /// base type of the object to create from the stream.
        private readonly Type elementType;

        /// when materializing a known type (like string) 
        /// <property> text-value </property>
        private readonly bool expectingSingleValue; 
 
        /// source reader (may be running on top of a buffer or be the real reader)
        private XmlReader reader; 

        /// untyped current object
        private object current;
 
        /// object being updated
        private object inserting; 
 
        /// identity of new objects and associated etag per enumerator.MoveNext()
        private Dictionary identityStack; 

        /// list of links to add
        private List links;
 
        /// debugging tool, # calls to ReadNext()
        private int version; 
 
        /// has GetEnumerator been called?
        private bool calledGetEnumerator; 

        /// stack of readers used when buffering intermediate entries (to create XElements and fire events)
        private Stack stackedReaders;
 
        /// 
        /// output writer, set using reflection 
        ///  
#if DEBUG && !ASTORIA_LIGHT
        private System.IO.TextWriter writer = new System.IO.StringWriter(System.Globalization.CultureInfo.InvariantCulture); 
#else
#pragma warning disable 649
        private System.IO.TextWriter writer;
#pragma warning restore 649 
#endif
 
        ///  
        /// constructor
        ///  
        /// originating context
        /// reader
        /// element base type
        /// merge option to use for this materialization pass 
        internal MaterializeAtom(DataServiceContext context, XmlReader reader, Type elementType, MergeOption mergeOption)
        { 
            Debug.Assert(null != reader, "null XmlReader"); 
            this.context = context;
            this.dataNamespace = reader.Settings.NameTable.Add(context.DataNamespace); 

            this.elementType = elementType;
            this.MergeOptionValue = mergeOption;
            this.ignoreMissingProperties = context.IgnoreMissingProperties; 
            this.reader = reader;
            this.expectingSingleValue = ClientConvert.IsKnownType(Nullable.GetUnderlyingType(elementType) ?? elementType); 
        } 

        /// atom parser state machine positions 
        private enum AtomParseState
        {
            /// None
            None, 

            /// Feed 
            Feed, 

            /// Entry 
            Entry,

            /// Content
            Content, 

            /// Complex 
            Complex, 

            /// Property 
            PropertyContent,

            /// Property
            PropertyComplex, // SQLBUDT 572585 

            /// LinkFeed 
            LinkFeed, 

            /// LinkEntry 
            LinkEntry,
        }

        #region Current 
        /// 
        /// untyped current object property 
        ///  
        public object Current
        { 
            get
            {
                return ClientConvert.VerifyCast(this.elementType, this.current);
            } 
        }
        #endregion 
 
        #region IDisposable
        ///  
        /// dispose
        /// 
        public void Dispose()
        { 
            this.current = null;
            this.inserting = null; 
 
            if (null != this.reader)
            { 
                ((IDisposable)this.reader).Dispose();
            }

            if (null != this.writer) 
            {
                this.writer.Dispose(); 
            } 
        }
        #endregion 

        #region IEnumerable
        /// 
        /// as IEnumerable 
        /// 
        /// this 
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
        {
            this.CheckGetEnumerator(); 
            return this;
        }
        #endregion
 
        /// 
        /// create the next object from the stream 
        ///  
        /// false if stream is finished
        public bool MoveNext() 
        {
            this.current = null;

            if (null != this.identityStack) 
            {
                this.identityStack.Clear(); 
            } 

            if (null != this.links) 
            {
                this.links.Clear();
            }
 
            bool result = false;
            if ((0 == this.version) && !this.expectingSingleValue && typeof(IEnumerable).IsAssignableFrom(this.elementType)) 
            { 
                Type implementationType = ClientType.GetImplementationType(this.elementType, typeof(ICollection<>));
                if (null != implementationType) 
                {
                    Type expectedType = implementationType.GetGenericArguments()[0]; // already know its IList<>
                    implementationType = this.elementType;
                    if (implementationType.IsInterface) 
                    {
                        implementationType = typeof(System.Collections.ObjectModel.Collection<>).MakeGenericType(expectedType); 
                    } 

                    IList list = (IList)Activator.CreateInstance(implementationType); 
                    object local = null;

                    EntityStates localState = 0;
                    while ((0 <= this.version) && 
                           this.reader.Read() &&
                           this.ReadNext(null, expectedType, AtomParseState.None, ref localState, ref local)) 
                    { 
                        list.Add(local);
                        local = null; 
                    }

                    Debug.Assert(this.version < 0, "should have completed the read");
                    this.current = list; 
                    result = true;
                } 
            } 

            if (null == this.current) 
            {
                EntityStates localState = 0;
                result = (0 <= this.version) &&
                          this.reader.Read() && 
                          this.ReadNext(null, this.elementType, AtomParseState.None, ref localState, ref this.current);
            } 
 
            if (MergeOption.NoTracking != this.MergeOptionValue)
            { 
                if (null != this.identityStack)
                {
                    foreach (KeyValuePair entity in this.identityStack)
                    { 
                        if (entity.Value.AttachToContext)
                        { 
                            this.context.AttachTo(entity.Key, entity.Value.EditLink, entity.Value.ETag, entity.Value.Entity, false); 
                        }
                    } 
                }

                if (null != this.links)
                { 
                    foreach (LinkDescriptor link in this.links)
                    { 
                        if (EntityStates.Added == link.State) 
                        {   // Added implies collection
                            if ((EntityStates.Deleted == this.context.GetEntity(link.Target).State) || 
                                (EntityStates.Deleted == this.context.GetEntity(link.Source).State))
                            {
                                this.context.DeleteLink(link.Source, link.SourceProperty, link.Target);
                            } 
                            else
                            { 
                                this.context.AttachLink(link.Source, link.SourceProperty, link.Target, this.MergeOptionValue); 
                            }
                        } 
                        else if (EntityStates.Modified == link.State)
                        {   // Modified implies reference
                            object target = link.Target;
                            if (MergeOption.PreserveChanges == this.MergeOptionValue) 
                            {
                                DataServiceContext.RelatedEnd end = this.context.GetLinks(link.Source, link.SourceProperty).FirstOrDefault(); 
                                if (null != end && null == end.TargetResouce) 
                                {   // leave the SetLink(link.Source, link.SourceProperty, null)
                                    continue; 
                                }

                                if ((null != target) && (EntityStates.Deleted == this.context.GetEntity(target).State) ||
                                    (EntityStates.Deleted == this.context.GetEntity(link.Source).State)) 
                                {
                                    target = null; 
                                } 
                            }
 
                            this.context.AttachLink(link.Source, link.SourceProperty, target, this.MergeOptionValue);
                        }
                        else
                        {   // detach link 
                            // Debug.Assert(
                            // (MergeOption.OverwriteChanges == this.MergeOptionValue) || 
                            // (MergeOption.PreserveChanges == this.MergeOptionValue && 
                            // EntityStates.Added != this.context.GetLink(link.Source, link.SourceProperty, link.Target).State),
                            // "only OverwriteChanges or PreserveChanges should remove existing links"); 
                            Debug.Assert(EntityStates.Detached == link.State, "not detached link");
                            this.context.DetachLink(link.Source, link.SourceProperty, link.Target);
                        }
                    } 
                }
            } 
 
            return result;
        } 

        /// 
        /// Not supported.
        ///  
        /// Always thrown
        void System.Collections.IEnumerator.Reset() 
        { 
            throw Error.NotSupported();
        } 

        /// set the inserted object expected in the response
        /// object being inserted that the response is looking for
        internal void SetInsertingObject(object addedObject) 
        {
            this.inserting = addedObject; 
        } 

        /// are the two values the same reference 
        /// value1
        /// value2
        /// true if they are the same reference
        private static bool AreSame(string value1, string value2) 
        {
            // bool result = Object.ReferenceEquals(value1, value2); 
            // Debug.Assert(result == (value1 == value2), "!NameTable - unable to do reference comparison on '" + value1 + "'"); 
            // XElement uses a global name table which may have encountered
            // our strings first and return a different instance than what was expected 
            bool result = (value1 == value2);
            return result;
        }
 
        /// is the reader on contentNode where the localName and namespaceUri match
        /// reader 
        /// localName 
        /// namespaceUri
        /// true if localName and namespaceUri match reader current element 
        private static bool AreSame(XmlReader reader, string localName, string namespaceUri)
        {
            Debug.Assert((null != reader) && (null != localName) && (null != namespaceUri), "null");
            return ((XmlNodeType.Element == reader.NodeType) || (XmlNodeType.EndElement == reader.NodeType)) && 
                    AreSame(reader.LocalName, localName) && AreSame(reader.NamespaceURI, namespaceUri);
        } 
 
        /// verify the GetEnumerator can only be called once
        private void CheckGetEnumerator() 
        {
            if (this.calledGetEnumerator)
            {
                throw Error.NotSupported(Strings.DataServiceException_GeneralError); 
            }
 
            this.calledGetEnumerator = true; 
        }
 
        /// 
        /// build up a single object result
        /// 
        /// what is the nested element type (collection support) 
        /// what is the expected base type for current object
        /// what is the initial parse state 
        /// entity state 
        /// current object being populated
        /// true if not at end of object 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506", Justification = "eventually need to rewrite")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "multipurpose variable")]
        private bool ReadNext(ClientType currentType, Type expectedType, AtomParseState atom, ref EntityStates entityState, ref object currentValue)
        { 
            Debug.Assert((null != this.reader) && !this.reader.EOF && (this.reader.NodeType != XmlNodeType.None), "bad XmlReader");
            this.version++; 
 
            // object level variables
            // elementType (this is our return point until the next MoveNext call) 
            string elementName = null;
            string namespaceUri = null;
            string etag = null;
            Uri identity = null; 
            Uri editLink = null;
            bool? mediaLinkEntry = null; 
            bool hasContentProperties = false; 
            XElement currentEntry = null;
            ArraySet haveSetKeyProperty; 

            // property level variables
            ClientType.ClientProperty property = null;
            string propertyName = null; 
            bool hasValue = false;
 
            ClientType nestedElementType = null; 
            object nestedValue = null;
            bool setNestedValue = false; 

#if ASTORIA_OPEN_OBJECT
            object openProperties = null; // like currentValue, this doesn't need to be nulled out
#endif 

            if ((null != currentType) && currentType.HasKeys) 
            { 
                haveSetKeyProperty = new ArraySet(currentType.KeyCount);
            } 
            else
            {
                haveSetKeyProperty = default(ArraySet);
            } 

            do 
            {   // caller of ReadNext called this.reader.Read() 
                string propertyValue = null;
                bool knownElement = false; 

                switch (this.reader.NodeType)
                {
                    #region Element 
                    case XmlNodeType.Element:
                        this.TraceElement(); 
 
                        this.CheckAgainstFailure();
 
                        elementName = this.reader.LocalName;
                        namespaceUri = this.reader.NamespaceURI;

                        if (this.expectingSingleValue) 
                        {
                            #region return known primitive/reference typed value 
                            // CreateQuery and the result is value 
                            // we don't really care what the namespace is though with astoria
                            // it would be XmlConstants.DataWebNamespace ("http://schemas.microsoft.com/ado/2007/08/dataservices") 
                            propertyValue = this.ReadElementString(true);
                            if (null != propertyValue)
                            {
                                currentValue = ClientConvert.ChangeType(propertyValue, Nullable.GetUnderlyingType(expectedType) ?? expectedType); 
                            }
 
                            this.version = -this.version; 
                            return true;
                            #endregion 
                        }

                        if (((AtomParseState.None == atom) || (AtomParseState.LinkFeed == atom)) &&
                            AreSame(XmlConstants.AtomNamespace, namespaceUri) && 
                            AreSame(XmlConstants.AtomFeedElementName, elementName))
                        { 
                            #region  
                            if (this.reader.IsEmptyElement)
                            { 
                                this.version = -this.version;
                                return false;
                            }
 
                            if (AtomParseState.None == atom)
                            {   // leave state in LinkFeed state so we exit properly when no  exist 
                                atom = AtomParseState.Feed; 
                            }
 
                            Debug.Assert(AtomParseState.Feed == atom || AtomParseState.LinkFeed == atom, "feed state");
                            knownElement = true;
                            #endregion
                        } 
                        else if (((AtomParseState.LinkEntry == atom) || (AtomParseState.LinkFeed == atom) || (AtomParseState.Feed == atom) || AtomParseState.None == atom) &&
                                 AreSame(XmlConstants.AtomNamespace, namespaceUri) && 
                                 AreSame(XmlConstants.AtomEntryElementName, elementName)) 
                        {
                            #region  
                            atom = AtomParseState.Entry;
                            this.PushBufferedReader(ref currentEntry);
                            currentType = this.ReadTypeAttribute(currentEntry, expectedType);
                            etag = this.reader.GetAttribute(XmlConstants.AtomETagAttributeName, XmlConstants.DataWebMetadataNamespace); 
                            if (currentType.HasKeys)
                            { 
                                haveSetKeyProperty = new ArraySet(currentType.KeyCount); 
                            }
 
                            knownElement = true;
                            #endregion
                        }
                        else if ((AtomParseState.Entry == atom) && 
                                 AreSame(XmlConstants.AtomNamespace, namespaceUri))
                        { 
                            if (AreSame(XmlConstants.AtomIdElementName, elementName)) 
                            {
                                #region resource uri 

                                // The "atom:id" element conveys a permanent, universally unique
                                // identifier for an entry or feed.
 
                                // Its content MUST be an IRI, as defined by [RFC3987].  Note that the
                                // definition of "IRI" excludes relative references.  Though the IRI 
                                // might use a dereferencable scheme, Atom Processors MUST NOT assume it 
                                // can be dereferenced.
                                Debug.Assert(null == currentValue, "already has an instance"); 
                                Debug.Assert(null == identity, "multiple ");

                                identity = Util.CreateUri(this.ReadElementString(false), UriKind.RelativeOrAbsolute);
                                if (!identity.IsAbsoluteUri) 
                                {
                                    throw Error.InvalidOperation(Strings.Context_TrackingExpectsAbsoluteUri); 
                                } 

                                // ReferenceIdentity is a test hook to help verify we dont' use identity instead of editLink 
                                identity = Util.ReferenceIdentity(identity);

                                knownElement = true;
                                #endregion 
                            }
                            else if (AreSame(XmlConstants.AtomContentElementName, elementName)) 
                            { 
                                #region 
                                propertyValue = this.reader.GetAttributeEx(XmlConstants.AtomContentSrcAttributeName, XmlConstants.AtomNamespace); 
                                if (propertyValue != null)
                                {
                                    // This is a media link entry
                                    // Note that in this case we don't actually use this URL (or the link/edit-media URL) 
                                    // for editing. We rely on the Astoria URL convention (/propname/$value or just /$value)
                                    if (!this.reader.IsEmptyElement) 
                                    { 
                                        throw Error.InvalidOperation(Strings.Deserialize_ExpectedEmptyMediaLinkEntryContent);
                                    } 

                                    mediaLinkEntry = true;
                                    knownElement = true;
                                } 
                                else
                                { 
                                    // This is a regular (non-media link) entry 
                                    if (mediaLinkEntry.HasValue && mediaLinkEntry.Value)
                                    { 
                                        // This means we saw a  element but now we have a Content element
                                        // that's not just a media link entry pointer (src)
                                        throw Error.InvalidOperation(Strings.Deserialize_ContentPlusPropertiesNotAllowed);
                                    } 

                                    mediaLinkEntry = false; 
 
                                    propertyValue = this.reader.GetAttributeEx(XmlConstants.AtomTypeAttributeName, XmlConstants.AtomNamespace);
                                    if (XmlConstants.MimeApplicationXml == propertyValue || XmlConstants.MimeApplicationAtom == propertyValue) 
                                    {
                                        if (this.ReadChildElement(XmlConstants.AtomPropertiesElementName, XmlConstants.DataWebMetadataNamespace))
                                        {
                                            this.TraceElement(); 
                                            hasContentProperties = true;
                                            atom = AtomParseState.Content; 
                                        } 
                                        else if (!AreSame(this.reader, XmlConstants.AtomContentElementName, XmlConstants.AtomNamespace))
                                        { 
                                            throw Error.InvalidOperation(Strings.Deserialize_NotApplicationXml);
                                        }

                                        knownElement = true; 
                                    }
                                } 
                                #endregion 
                            }
                            else if (AreSame(XmlConstants.AtomLinkElementName, elementName)) 
                            {
                                string rel = this.reader.GetAttributeEx(XmlConstants.AtomLinkRelationAttributeName, XmlConstants.AtomNamespace);
                                if (XmlConstants.AtomEditRelationAttributeValue == rel)
                                { 
                                    #region uri
                                    editLink = Util.CreateUri(this.reader.GetAttributeEx(XmlConstants.AtomHRefAttributeName, XmlConstants.AtomNamespace), UriKind.RelativeOrAbsolute); 
                                    #endregion 
                                }
                                else if (!this.reader.IsEmptyElement) 
                                {
                                    propertyName = UriUtil.GetNameFromAtomLinkRelationAttribute(rel);

                                    #region  
                                    if (null != propertyName)
                                    { 
                                        Debug.Assert(!setNestedValue, "should not be set going in link"); 

                                        property = currentType.GetProperty(propertyName, this.ignoreMissingProperties); // may throw 
                                        if (null == property)
                                        {
                                            propertyName = null;
                                        } 
                                        else
                                        { 
                                            if (property.IsKnownType) 
                                            {
                                                throw Error.InvalidOperation(Strings.Deserialize_MismatchAtomLinkLocalSimple); 
                                            }

                                            if (this.ResolveOrCreateInstance(currentType, identity, editLink, etag, ref currentValue, ref entityState))
                                            { 
                                                atom = AtomParseState.Feed;
                                                this.FireEventAndPopBufferedReader(currentValue, ref currentEntry); 
                                                return true; 
                                            }
 
                                            object parent = currentValue;
                                            Type nestedType = property.PropertyType;
                                            propertyValue = this.reader.GetAttribute(XmlConstants.AtomTypeAttributeName);
                                            if (String.Equals("application/atom+xml;type=feed", propertyValue)) 
                                            {   // parent becomes the collection, not the currentValue
                                                #region LinkFeed 
#if ASTORIA_OPEN_OBJECT 
                                                if (property.OpenObjectProperty)
                                                {   // get the nested IList from the dictionary 
                                                    nestedType = typeof(OpenObject);
                                                    ((IDictionary)property.GetValue(currentValue)).TryGetValue(propertyName, out parent);
                                                    if ((null != parent) && !(parent is IList))
                                                    { 
                                                        throw Error.InvalidOperation(Strings.Deserialize_MismatchAtomLinkFeedPropertyNotCollection(propertyName));
                                                    } 
                                                } 
                                                else
#endif 
                                                if (null == (nestedType = property.CollectionType))
                                                {
                                                    throw Error.InvalidOperation(Strings.Deserialize_MismatchAtomLinkFeedPropertyNotCollection(propertyName));
                                                } 
                                                else
                                                {   // get the nested IList from the current value 
                                                    parent = property.GetValue(currentValue); 
                                                }
 
                                                if (null == parent)
                                                {
                                                    setNestedValue = true;
 
                                                    // if the current property is not settable
                                                    // we fail after populating contents into a collection 
                                                    Type implementationType; 
#if ASTORIA_OPEN_OBJECT
                                                    if (property.OpenObjectProperty) 
                                                    {
                                                        implementationType = typeof(System.Collections.ObjectModel.Collection);
                                                    }
                                                    else 
#endif
                                                    { 
                                                        implementationType = property.PropertyType; 
                                                        if (implementationType.IsInterface)
                                                        { 
                                                            implementationType = typeof(System.Collections.ObjectModel.Collection<>).MakeGenericType(nestedType);
                                                        }
                                                    }
 
                                                    parent = Activator.CreateInstance(implementationType);
                                                } 
 
                                                atom = AtomParseState.LinkFeed;
                                                #endregion 
                                            }
                                            else if (String.Equals("application/atom+xml;type=entry", propertyValue))
                                            {
                                                #region LinkEntry 
#if ASTORIA_OPEN_OBJECT
                                                if (property.OpenObjectProperty) 
                                                { 
                                                    nestedType = typeof(OpenObject);
                                                } 
                                                else
#endif
                                                if (null != property.CollectionType)
                                                { 
                                                    throw Error.InvalidOperation(Strings.Deserialize_MismatchAtomLinkEntryPropertyIsCollection(propertyName));
                                                } 
 
                                                atom = AtomParseState.LinkEntry;
                                                #endregion 
                                            }

                                            int linkCount = 0;
                                            AtomParseState linkStart = atom; 

                                            #region read  
                                            // Check for empty inline element - that indicates that the value is null 
                                            // This is only true for reference properties
                                            if (this.ReadChildElement(XmlConstants.AtomInlineElementName, XmlConstants.DataWebMetadataNamespace)) 
                                            {
                                                this.TraceElement();

                                                Debug.Assert( 
                                                    (currentValue == parent && AtomParseState.LinkEntry == atom) ||
                                                    (currentValue != parent && AtomParseState.LinkFeed == atom && parent is IEnumerable), 
                                                    "unexpected parent"); 

                                                Debug.Assert(null == nestedValue, "nestedValue should be null before reading links"); 
                                                Debug.Assert(AtomParseState.LinkFeed == atom || AtomParseState.LinkEntry == atom, "expecting LinkEntry or LinkFeed");

                                                if (!this.reader.IsEmptyElement)
                                                { 
                                                    #region read 
                                                    EntityStates nestedState = 0; 
                                                    while ((0 <= this.version) && 
                                                           this.reader.Read() &&
                                                           this.ReadNext(null, nestedType, atom, ref nestedState, ref nestedValue)) 
                                                    {
                                                        // will currentValue.Collection.Add or currentValue.set_Reference
                                                        if ((MergeOption.AppendOnly != this.MergeOptionValue) ||
                                                            (EntityStates.Detached == entityState)) 
                                                        {
#if ASTORIA_OPEN_OBJECT 
                                                            if ((setNestedValue || parent is IList) && property.OpenObjectProperty) 
                                                            {
                                                                ((IList)parent).Add((OpenObject)nestedValue); 
                                                            }
                                                            else
                                                            {
                                                                property.SetValue(parent, nestedValue, propertyName, ref openProperties, true); 
                                                            }
#else 
                                                            property.SetValue(parent, nestedValue, propertyName, true); 
#endif
 
                                                            // if null == nestedValue, that implies an existing reference may now be gone
                                                            // however, since we don't "refresh" - we leave that previous relationship intact
                                                            if ((null != nestedValue) &&
#if ASTORIA_OPEN_OBJECT 
                                                                 !property.OpenObjectProperty &&
#endif 
 currentType.HasKeys && ClientType.Create(nestedValue.GetType()).HasKeys) 
                                                            {
                                                                if (null == this.links) 
                                                                {
                                                                    this.links = new List();
                                                                }
 
                                                                // cache the links we want to add - but can't until after both ends exist in the context
                                                                EntityStates linkState = (AtomParseState.LinkEntry == linkStart) ? EntityStates.Modified : EntityStates.Added; 
                                                                this.links.Add(new LinkDescriptor(currentValue, property.PropertyName, nestedValue, linkState)); 
                                                            }
                                                        } 

                                                        linkCount++;
                                                        nestedValue = null;
                                                        atom = AtomParseState.LinkEntry; 

                                                        if ((AtomParseState.LinkEntry == linkStart) && (0 != linkCount)) 
                                                        {   //  
                                                            break; // while
                                                        } 
                                                    }
                                                    #endregion
                                                }
 
                                                #region no links
                                                if ((0 == linkCount) && (AtomParseState.LinkEntry == linkStart)) 
                                                {   // set null nested value 
                                                    if ((MergeOption.AppendOnly != this.MergeOptionValue) ||
                                                        (EntityStates.Detached == entityState)) 
                                                    {
                                                        if (null == this.links)
                                                        {
                                                            this.links = new List(); 
                                                        }
 
                                                        // AppendOnly & NoTracking - only if creating new object 
                                                        // OverwriteChanges - server wins
                                                        // PreserveChanges - new object, non-null target -> unchanged, null target -> modified 
                                                        Debug.Assert(
                                                            (MergeOption.AppendOnly == this.MergeOptionValue && EntityStates.Detached == entityState) ||
                                                            (MergeOption.NoTracking == this.MergeOptionValue && EntityStates.Detached == entityState) ||
                                                            (MergeOption.OverwriteChanges == this.MergeOptionValue) || 
                                                            (MergeOption.PreserveChanges == this.MergeOptionValue),
                                                            "unexpected merge of link reference"); 
 
                                                        Debug.Assert(null == nestedValue, "expecting null nestedValue");
                                                        this.links.Add(new LinkDescriptor(currentValue, property.PropertyName, null, EntityStates.Modified)); 
                                                        parent = null;
                                                        setNestedValue = true;
                                                    }
                                                } 
                                                else if ((AtomParseState.LinkFeed == linkStart) &&
                                                    (MergeOption.OverwriteChanges == this.MergeOptionValue || MergeOption.PreserveChanges == this.MergeOptionValue)) 
                                                {   // detach extra remaining links 
                                                    object collection = null;
                                                    var q = (from x in this.context.GetLinks(currentValue, property.PropertyName) 
                                                             where MergeOption.OverwriteChanges == this.MergeOptionValue || EntityStates.Added != x.State
                                                             select new LinkDescriptor(x.SourceResource, x.SourceProperty, x.TargetResouce, EntityStates.Detached))
                                                            .Except(this.links, LinkDescriptor.EquivalentComparer);
 
                                                    foreach (LinkDescriptor p in q)
                                                    { 
                                                        if (null == collection) 
                                                        {
                                                            collection = property.GetValue(currentValue); 
                                                        }

                                                        if (null != collection)
                                                        { 
                                                            property.RemoveValue(collection, p.Target);
                                                        } 
 
                                                        this.links.Add(p);
                                                    } 
                                                }
                                                #endregion

                                                if (setNestedValue) 
                                                {
                                                    nestedValue = parent; 
                                                } 

                                                Debug.Assert(this.reader.IsEmptyElement || XmlNodeType.EndElement == this.reader.NodeType, "expected EndElement"); 
                                                Debug.Assert(
                                                    (this.reader.IsEmptyElement && AreSame(XmlConstants.AtomInlineElementName, this.reader.LocalName)) ||
                                                    (AtomParseState.LinkFeed == linkStart && AreSame(XmlConstants.AtomFeedElementName, this.reader.LocalName)) ||
                                                    (AtomParseState.LinkEntry == linkStart && AreSame(XmlConstants.AtomEntryElementName, this.reader.LocalName)), 
                                                    "link didn't end on expected element");
                                            } 
 
                                            this.SkipToEnd(XmlConstants.AtomLinkElementName, XmlConstants.AtomNamespace);
                                            #endregion 

                                            Debug.Assert(((XmlNodeType.Element == this.reader.NodeType && this.reader.IsEmptyElement) ||
                                                          (XmlNodeType.EndElement == this.reader.NodeType)) &&
                                                          AreSame(this.reader, XmlConstants.AtomLinkElementName, XmlConstants.AtomNamespace), 
                                                         "didn't land on ");
 
                                            #region Set collection / reference instance 

                                            if (setNestedValue) 
                                            {
#if ASTORIA_OPEN_OBJECT
                                                property.SetValue(currentValue, nestedValue, propertyName, ref openProperties, false);
#else 
                                                property.SetValue(currentValue, nestedValue, propertyName, false);
#endif 
                                                nestedValue = null; 
                                            }
                                            #endregion 

                                            setNestedValue = false;
                                            propertyName = null;
                                            property = null; 
                                            Debug.Assert(null == nestedValue, "null == nestedValue");
                                            knownElement = true; 
                                        } 
                                    }
 
                                    atom = AtomParseState.Entry;
                                    #endregion
                                }
                            } 
                        }
                        else if ((AtomParseState.Entry == atom) && 
                                 AreSame(XmlConstants.DataWebMetadataNamespace, namespaceUri) && 
                                 AreSame(XmlConstants.AtomPropertiesElementName, elementName))
                        { 
                            #region 
                            if (mediaLinkEntry.HasValue && !mediaLinkEntry.Value)
                            {
                                // This means we saw a non-empty  element but now we have a Properties element 
                                // that also carries properties
                                throw Error.InvalidOperation(Strings.Deserialize_ContentPlusPropertiesNotAllowed); 
                            } 

                            mediaLinkEntry = true; 

                            if (!this.reader.IsEmptyElement)
                            {
                                atom = AtomParseState.Content; // Semantically this is equivalent to content, so treat it the same 
                            }
 
                            knownElement = true; 
                            #endregion
                        } 
                        else if (((AtomParseState.Content == atom) || (AtomParseState.Complex == atom)) &&
                                 AreSame(this.dataNamespace, namespaceUri))
                        {
                            if (this.ResolveOrCreateInstance(currentType, identity, editLink, etag, ref currentValue, ref entityState)) 
                            {
                                atom = AtomParseState.Feed; 
                                this.FireEventAndPopBufferedReader(currentValue, ref currentEntry); 
                                return true;
                            } 

                            #region MergeOption checks
                            Debug.Assert(null != currentValue, "null currentValue");
                            Debug.Assert(0 != entityState, "expected entitystate"); 

                            if (((MergeOption.AppendOnly == this.MergeOptionValue) && (EntityStates.Detached != entityState)) || 
                                ((MergeOption.PreserveChanges == this.MergeOptionValue) && (EntityStates.Added == entityState || EntityStates.Modified == entityState))) 
                            {   // do not change content in this state
                                this.SkipToEnd(this.reader.LocalName, this.reader.NamespaceURI); 
                                break;
                            }

                            Debug.Assert( 
                                (MergeOption.NoTracking == this.MergeOptionValue && EntityStates.Detached == entityState) ||
                                (MergeOption.AppendOnly == this.MergeOptionValue && EntityStates.Detached == entityState) || 
                                (MergeOption.PreserveChanges == this.MergeOptionValue && (EntityStates.Detached == entityState || EntityStates.Unchanged == entityState || EntityStates.Deleted == entityState)) || 
                                (MergeOption.OverwriteChanges == this.MergeOptionValue),
                                "NoTracking and AppendOnly, PreserveChanges should not change content of existing objects"); 
                            #endregion

                            #region 
                            property = currentType.GetProperty(elementName, this.ignoreMissingProperties); // may throw 
                            if (null != property)
                            { 
                                propertyName = elementName; 
                                atom = (AtomParseState.Content == atom) ? AtomParseState.PropertyContent : AtomParseState.PropertyComplex;
 
                                if (property.KeyProperty)
                                {
                                    haveSetKeyProperty.Add(propertyName, null);
                                } 

#if ASTORIA_OPEN_OBJECT 
                                if (property.IsKnownType || 
                                    ClientConvert.IsKnownType((nestedElementType = this.ReadTypeAttribute(property.OpenObjectProperty ? typeof(OpenObject) : property.PropertyType)).ElementType))
#else 
                                if (property.IsKnownType ||
                                    ClientConvert.IsKnownType((nestedElementType = this.ReadTypeAttribute(property.PropertyType)).ElementType))
#endif
                                { 
                                    // propertyValue
                                    propertyValue = this.ReadElementString(true); 
 
                                    object value = propertyValue;
                                    if (null != propertyValue) 
                                    {
                                        value = ClientConvert.ChangeType(propertyValue, (null != nestedElementType ? nestedElementType.ElementType : property.PropertyType));
                                    }
 
#if ASTORIA_OPEN_OBJECT
                                    property.SetValue(currentValue, value, propertyName, ref openProperties, false); 
#else 
                                    property.SetValue(currentValue, value, propertyName, false);
#endif 

                                    atom = (AtomParseState.PropertyContent == atom) ? AtomParseState.Content : AtomParseState.Complex;
                                    property = null;
                                    propertyName = null; 
                                    nestedElementType = null;
                                    hasValue = false; 
                                } 
                                else if (this.reader.IsEmptyElement)
                                { 
#if ASTORIA_OPEN_OBJECT
                                    property.SetValue(currentValue, null, propertyName, ref openProperties, false);
#else
                                    property.SetValue(currentValue, null, propertyName, false); 
#endif
                                } 
                                else 
                                {
#if ASTORIA_OPEN_OBJECT 
                                    if (!property.OpenObjectProperty && (null != property.CollectionType))
#else
                                    if (null != property.CollectionType)
#endif 
                                    {   // client object property is collection implying nested collection of complex objects
                                        throw Error.NotSupported(Strings.ClientType_CollectionOfNonEntities); 
                                    } 

                                    #region recurse into complex type 
                                    if (null == nestedValue)
                                    {   // retreived only once per collection or nested object
                                        nestedValue = property.GetValue(currentValue);
 
#if ASTORIA_OPEN_OBJECT
                                        if (property.OpenObjectProperty) 
                                        { 
                                            if (null != nestedValue)
                                            { 
                                                ((IDictionary)nestedValue).TryGetValue(propertyName, out nestedValue);
                                            }
                                            else
                                            { 
                                                throw Error.InvalidOperation(Strings.Collection_NullCollectionReference(currentType.ElementTypeName, property.PropertyName));
                                            } 
                                        } 
#endif
                                    } 

                                    if (null == nestedValue)
                                    {   // when the value is null, assume the recursion will create the object
                                        hasValue = true; 
                                    }
 
                                    if (this.reader.Read() && 
                                        this.ReadNext(nestedElementType, nestedElementType.ElementType, AtomParseState.Complex, ref entityState, ref nestedValue))
                                    { 
                                        setNestedValue = (hasValue = (nestedValue != null));
                                        hasValue = true;
                                        goto case XmlNodeType.EndElement;
                                    } 
                                    #endregion
                                } 
 
                                knownElement = true;
                            } 
                            #endregion
                        }
                        else if ((AtomParseState.None == atom) && AreSame(this.dataNamespace, namespaceUri) && (null == currentType))
                        {   // complex type? 
                            Debug.Assert(0 == entityState, "shouldn't have existing entity");
                            ClientType tmp = this.ReadTypeAttribute(expectedType); 
                            if (!tmp.HasKeys) 
                            {
                                currentType = tmp; 
                                if (this.reader.IsEmptyElement || this.DoesNullAttributeSayTrue())
                                {
                                    this.SkipToEnd(this.reader.LocalName, this.reader.NamespaceURI);
                                    return true; 
                                }
 
                                atom = AtomParseState.Complex; 
                                entityState = EntityStates.Detached;
                                knownElement = true; 
                            }
                        }

                        if (!knownElement) 
                        {
                            this.SkipToEnd(this.reader.LocalName, this.reader.NamespaceURI); 
                        } 

                        break; 

                    #endregion

                    #region Text 
                    case XmlNodeType.Text:
                    case XmlNodeType.SignificantWhitespace: 
                        Debug.Assert(!this.expectingSingleValue, "!this.expectingSingleValue, Text"); 

                        // throw an exception that we hit mixed-content 
                        // propertyValueelementText
                        throw Error.InvalidOperation(Strings.Deserialize_MixedContent(currentType.ElementTypeName));
                    #endregion
 
                    #region EndElement
                    case XmlNodeType.EndElement: 
                        Debug.Assert(!this.expectingSingleValue, "!this.expectingSingleValue"); 
                        this.TraceEndElement(true);
 
                        if (null == currentType)
                        {   // 
                            // or reader started in a bad state (which is okay)
                            Debug.Assert(!hasValue, "EndElement w/ haveValue"); 
                            Debug.Assert(!setNestedValue, "EndElement w/ setNestedValue");
                            Debug.Assert(null == property, "EndElement w/ property"); 
                            Debug.Assert(null == propertyName, "EndElement w/ propertyName"); 
                            Debug.Assert(null == nestedValue, "EndElement w/ nestedValue");
                            Debug.Assert(null == nestedElementType, "EndElement w/ nestedElementType"); 

                            // SQLBUDT 565573
                            if (AtomParseState.LinkEntry != atom && AtomParseState.LinkFeed != atom)
                            { 
                                atom = AtomParseState.None;
                                this.version = -this.version; 
                            } 

                            return false; 
                        }

                        if (null == property)
                        {   //  
                            Debug.Assert(!hasValue, "EndElement property w/ haveValue");
                            Debug.Assert(!setNestedValue, "EndElement property w/ setNestedValue"); 
                            Debug.Assert(null == property, "EndElement property w/ propertyName"); 
                            Debug.Assert(null == nestedValue, "EndElement property w/ nestedValue");
                            Debug.Assert(null == nestedElementType, "EndElement property w/ nestedElementType"); 
                            Debug.Assert(null != currentType, "Should have derived a type from the entry or from the query");

                            if (AtomParseState.Content == atom)
                            { 
                                if (hasContentProperties)
                                { 
                                    this.SkipToEnd(XmlConstants.AtomContentElementName, XmlConstants.AtomNamespace); 
                                }
 
                                atom = AtomParseState.Entry;
                                continue;
                            }
                            else if (AtomParseState.Complex == atom) 
                            {
                                return true; 
                            } 
                            else if (AtomParseState.Entry == atom)
                            { 
                                this.SkipToEnd(XmlConstants.AtomEntryElementName, XmlConstants.AtomNamespace);
                                atom = AtomParseState.Feed;

                                // it might happen that we see no data properties in an entry, just 
                                // metadata such as the ID. In the case were no properties where present
                                // we just create an uninitialized entry and attach it if needed 
                                this.ResolveOrCreateInstance(currentType, identity, editLink, etag, ref currentValue, ref entityState); 

                                // validate all keys have been set 
                                if (currentType.HasKeys && (EntityStates.Added == entityState || EntityStates.Detached == entityState))
                                {
                                    foreach (ClientType.ClientProperty p in currentType.Properties)
                                    { 
                                        // if property is a key and not set
                                        if (p.KeyProperty && (null == haveSetKeyProperty.Remove(p.PropertyName, String.Equals))) 
                                        { 
                                            throw Error.InvalidOperation(Strings.BatchStream_ContentExpected(p.PropertyName));
                                        } 
                                    }
                                }

                                this.FireEventAndPopBufferedReader(currentValue, ref currentEntry); 
                                return true;
                            } 
 
                            Debug.Assert(false, "invalid Atom state");
                            this.version = -this.version; 
                            Error.ThrowInternalError(InternalError.UnexpectedReadState);
                        }

                        //  
                        Debug.Assert(null != currentValue, "null currentValue");
                        Debug.Assert(null != propertyName, "null propertyName"); 
                        Debug.Assert(!setNestedValue || (null != property), "null property w/ nestedValue"); 

                        if ((null != nestedValue) && 
                            (setNestedValue ||
#if ASTORIA_OPEN_OBJECT
                              property.OpenObjectProperty ||
#endif 
 nestedValue.GetType().IsValueType))
                        { 
                            Debug.Assert(null != nestedValue, "EndElement after nested w/o nestedValue"); 

                            // if hasValue is false, we still want to set the nestedValue instance 
                            // 

                            // which is different than 
                            // which would have set null for nested complex property 
#if ASTORIA_OPEN_OBJECT
                            property.SetValue(currentValue, nestedValue, propertyName, ref openProperties, false); 
#else 
                            property.SetValue(currentValue, nestedValue, propertyName, false);
#endif 
                        }
                        else if (!hasValue)
                        {   // 
                            Debug.Assert(null != property, "null property without value"); 
#if ASTORIA_OPEN_OBJECT
                            property.SetValue(currentValue, null, propertyName, ref openProperties, false); 
#else 
                            property.SetValue(currentValue, null, propertyName, false);
#endif 
                        }

                        // finished reading property, continue parsing element
                        Debug.Assert( 
                            AtomParseState.PropertyContent == atom ||
                            AtomParseState.PropertyComplex == atom, 
                            "expected atom property state"); 
                        atom = (AtomParseState.PropertyContent == atom) ? AtomParseState.Content : AtomParseState.Complex;
                        property = null; 
                        propertyName = null;
                        hasValue = false;

                        nestedElementType = null; 
                        nestedValue = null;
                        setNestedValue = false; 
                        break; 
                    #endregion
 
                    #region fail on unhandled content nodes, similar to XmlReader.MoveToContent()
                    case XmlNodeType.CDATA:
                    case XmlNodeType.EntityReference:
                    case XmlNodeType.EndEntity: 
                        // Checks whether the current node is a content (non-whitespace text, CDATA, Element, EndElement, EntityReference
                        // or EndEntity) node. If the node is not a content node, then the method skips ahead to the next content node or 
                        // end of file. Skips over nodes of type ProcessingInstruction, DocumentType, Comment, Whitespace and SignificantWhitespace. 
                        this.version = -this.version;
                        Error.ThrowInternalError(InternalError.UnexpectedXmlNodeTypeWhenReading); 
                        break;

                    #endregion
                } 
            }
            while (this.reader.Read()); 
 
            Debug.Assert(null == currentValue, "non-null currentValue");
            this.version = -this.version; 
            return false;
        }

        ///  
        /// create an instance and associate its identity for callstack identity resolution
        ///  
        /// type to create 
        /// identity of instance
        /// editLink of instance 
        /// etag of identity
        /// current value instance
        /// entity state
        /// new instance of type 
        private bool ResolveOrCreateInstance(ClientType type, Uri identity, Uri editLink, string etag, ref object currentValue, ref EntityStates state)
        { 
            if (null == currentValue) 
            {
                if (null != identity) 
                {
                    #region handle POST entity response
                    if (null != this.inserting)
                    { 
                        Debug.Assert(MergeOption.OverwriteChanges == this.MergeOptionValue, "only expected value during SaveChanges");
 
                        // always attach an identity, so that inserted relationships can also happen 
                        // however, if NoTracking then its left in the inserted bucket
                        this.context.AttachIdentity(identity, editLink, this.inserting, etag); 
                    }
                    #endregion

                    if (type.HasKeys) 
                    {
                        Debug.Assert(null != identity, "missing identity"); 
                        if (MergeOption.NoTracking != this.MergeOptionValue) 
                        {
                            #region handle POST entity response 
                            if (null != this.inserting)
                            {
                                Debug.Assert(null == currentValue, "it already has a value?");
                                currentValue = this.inserting; 
                                state = EntityStates.Unchanged;
                                this.inserting = null; 
 
                                Debug.Assert(type.HasKeys, "!HasKeys");
                                Debug.Assert(type.ElementType.IsInstanceOfType(currentValue), "!IsInstanceOfType"); 
                            }
                            #endregion

                            if (null != (currentValue ?? (currentValue = this.context.TryGetEntity(identity, etag, this.MergeOptionValue, out state)))) 
                            {
                                if (!type.ElementType.IsInstanceOfType(currentValue)) 
                                { 
                                    throw Error.InvalidOperation(Strings.Deserialize_Current(type.ElementType, currentValue.GetType()));
                                } 
                            }
                        }
                    }
 
                    if ((null == currentValue) && (null != this.identityStack))
                    {   // always do identity resolution on the callstack within a top-level entity 
                        // but avoid the AppendOnly behavior for deep expanded items 
                        EntityWithTag pair;
                        this.identityStack.TryGetValue(identity, out pair); 
                        currentValue = pair.Entity;
                        state = pair.State;
                    }
                } 
                else
                { 
                    Debug.Assert(!type.HasKeys, "entity without identity"); 
                }
 
                if ((null == currentValue) || (this.MergeOptionValue == MergeOption.OverwriteChanges))
                {
                    if (null == currentValue)
                    { 
                        currentValue = type.CreateInstance();
                        state = EntityStates.Detached; 
                    } 

                    if (null != identity) 
                    {
                        if (null == this.identityStack)
                        {
                            this.identityStack = new Dictionary(); 
                        }
 
                        this.identityStack.Add(identity, new EntityWithTag(currentValue, editLink, etag, state, type.HasKeys)); 
                    }
                } 
            }

            return false;
        } 

        ///  
        /// Read subtree into an XElement, push the reader into the stack and set the current 
        /// reader to a reader on top of the XElement
        ///  
        /// Current XElement being processed
        private void PushBufferedReader(ref XElement currentEntry)
        {
            XmlReader subtree = this.reader.ReadSubtree(); 
            try
            { 
                currentEntry = XElement.Load(subtree); 
            }
            catch (XmlException ex) 
            {
                throw new DataServiceClientException(Strings.Deserialize_ServerException1, ex);
            }
            finally 
            {
                subtree.Close(); 
            } 

            if (this.stackedReaders == null) 
            {
                this.stackedReaders = new Stack();
            }
 
            this.stackedReaders.Push(this.reader);
            this.reader = currentEntry.CreateReader(); 
            this.reader.Read(); 
        }
 
        /// 
        /// Fire the reader event with the current buffered subtree reader and then pop
        /// the next reader in the stack
        ///  
        /// Current .NET object being read
        /// Buffered entry with data from the input stream 
        private void FireEventAndPopBufferedReader(object currentValue, ref XElement currentEntry) 
        {
            if (this.context.HasReadingEntityHandlers) 
            {
                Debug.Assert(currentEntry != null && currentValue != null, "Event present but did not create buffered entry?");
                Debug.Assert(this.stackedReaders.Count > 0, "Event present but previous reader not pushed?");
 
                this.context.FireReadingEntityEvent(currentValue, currentEntry);
            } 
 
            this.reader = this.stackedReaders.Pop();
            currentEntry = null; 
        }

        /// 
        /// Gets the type attribte and resolves the type from the current XmlReader. 
        /// 
        /// expected base type 
        /// resolved type 
        private ClientType ReadTypeAttribute(Type expectedType)
        { 
            string typeName = this.reader.GetAttribute(XmlConstants.AtomTypeAttributeName, XmlConstants.DataWebMetadataNamespace);
            Type resolvedType = this.context.ResolveTypeFromName(typeName, expectedType);
            return ClientType.Create(resolvedType);
        } 

        ///  
        /// Gets the type attribte and resolves the type from the specified . 
        /// 
        /// XML element for the ATOM entry. 
        /// expected base type
        /// resolved type
        private ClientType ReadTypeAttribute(XElement entryElement, Type expectedType)
        { 
            Debug.Assert(entryElement != null, "entryElement != null");
 
            // XNamespace.None is used for attribute because they are not namespace-qualified in the payloads. 
            XNamespace atomNs = XmlConstants.AtomNamespace;
            XName categoryName = atomNs + XmlConstants.AtomCategoryElementName; 
            XName schemeName = XNamespace.None + XmlConstants.AtomCategorySchemeAttributeName;
            XName termName = XNamespace.None + XmlConstants.AtomCategoryTermAttributeName;
            string typeSchemeText = this.context.TypeScheme.OriginalString;
 
            var categories = entryElement.Elements(categoryName);
            var category = categories.Where(c => c.Attributes(schemeName).Any(a => a.Value == typeSchemeText)).FirstOrDefault(); 
            var categoryTitle = (category == null) ? null : category.Attributes(termName).FirstOrDefault(); 

            string typeName = (categoryTitle == null) ? null : categoryTitle.Value; 
            Type resolvedType = this.context.ResolveTypeFromName(typeName, expectedType);
            return ClientType.Create(resolvedType);
        }
 
        /// 
        /// it can concatenate multiple adjacent text and CDATA blocks 
        /// ignores comments and whitespace 
        /// will leave reader on EndElement
        ///  
        /// check for null attribute or not
        /// text contained in the element. An null string if the element was empty
        private string ReadElementString(bool checkNullAttribute)
        { 
            Debug.Assert(XmlNodeType.Element == this.reader.NodeType, "not positioned on Element");
 
            string result = null; 
            bool empty = checkNullAttribute && !this.DoesNullAttributeSayTrue();
 
            if (this.reader.IsEmptyElement)
            {
                return (empty ? String.Empty : null);
            } 

            while (this.reader.Read()) 
            { 
                switch (this.reader.NodeType)
                { 
                    case XmlNodeType.EndElement:
                        this.TraceEndElement(false);
                        return result ?? (empty ? String.Empty : null);
                    case XmlNodeType.CDATA: 
                    case XmlNodeType.Text:
                    case XmlNodeType.SignificantWhitespace: 
                        if (null != result) 
                        {
                            throw Error.InvalidOperation(Strings.Deserialize_MixedTextWithComment); 
                        }

                        this.TraceText(this.reader.Value);
                        result = this.reader.Value; 
                        break;
                    case XmlNodeType.Comment: 
                    case XmlNodeType.Whitespace: 
                        break;
 
                    #region XmlNodeType error
                    case XmlNodeType.Element:
                        this.CheckAgainstFailure();
                        goto default; 

                    default: 
                        this.version = -this.version; 
                        throw Error.InvalidOperation(Strings.Deserialize_ExpectingSimpleValue);
                    #endregion 
                }
            }

            // xml ended before EndElement? 
            this.version = -this.version;
            throw Error.InvalidOperation(Strings.Deserialize_ExpectingSimpleValue); 
        } 

        /// Move to top-level child element of the expected type 
        /// localName of expected child content-node
        /// namespaceUri of expected child content-node
        /// true if moved to expected child content node
        private bool ReadChildElement(string localName, string namespaceUri) 
        {
            return (!this.reader.IsEmptyElement && 
                    (this.reader.NodeType != XmlNodeType.EndElement) && 
                    this.reader.Read() &&
                    this.reader.IsStartElement(localName, namespaceUri)); 
        }

        /// 
        /// Skips the children of the current node. 
        /// 
        /// local name of the node we search to 
        /// namespace of the node we search to 
        private void SkipToEnd(string localName, string namespaceURI)
        { 
            // 
            // 
            // 
            XmlReader xmlReader = this.reader; 
            int readerDepth = xmlReader.Depth;
 
            do 
            {
                switch (xmlReader.NodeType) 
                {
                    case XmlNodeType.Element:
                        this.CheckAgainstFailure();
                        if (xmlReader.IsEmptyElement && 
                            xmlReader.Depth == readerDepth &&
                            AreSame(xmlReader, localName, namespaceURI)) 
                        { 
                            return;
                        } 

                        break;

                    case XmlNodeType.EndElement: 
                        if (xmlReader.Depth <= readerDepth)
                        {   // new end element 
                            this.TraceEndElement(true); 
                            readerDepth--;
 
                            if (AreSame(xmlReader, localName, namespaceURI))
                            {
                                return;
                            } 
                        }
 
                        break; 
                }
            } 
            while (xmlReader.Read());
        }

        /// throw if current element is Exception 
        /// if current element is exception
        private void CheckAgainstFailure() 
        { 
            Debug.Assert(XmlNodeType.Element == this.reader.NodeType, "not Element");
            if (AreSame(this.reader, XmlConstants.XmlErrorElementName, XmlConstants.DataWebMetadataNamespace)) 
            {
                string message = this.ReadErrorMessage();
                this.version = -this.version;
 
                // In case of instream errors, the status code should be 500 (which is the default)
                throw new DataServiceClientException(Strings.Deserialize_ServerException(message)); 
            } 
        }
 
        /// With the reader positioned on an 'error' element, reads the text of the 'message' child.
        /// The text of the 'message' child element, empty if not found.
        private string ReadErrorMessage()
        { 
            Debug.Assert(AreSame(this.reader, XmlConstants.XmlErrorElementName, XmlConstants.DataWebMetadataNamespace), "AreSame(this.reader, XmlConstants.XmlErrorElementName, XmlConstants.DataWebMetadataNamespace)");
            int depth = 1; 
            while (depth > 0 && this.reader.Read()) 
            {
                if (this.reader.NodeType == XmlNodeType.Element) 
                {
                    if (!this.reader.IsEmptyElement)
                    {
                        depth++; 
                    }
 
                    if (depth == 2 && 
                        AreSame(this.reader, XmlConstants.XmlErrorMessageElementName, XmlConstants.DataWebMetadataNamespace))
                    { 
                        return this.ReadElementString(false);
                    }
                }
                else if (this.reader.NodeType == XmlNodeType.EndElement) 
                {
                    depth--; 
                } 
            }
 
            return String.Empty;
        }

        ///  
        /// check the atom:null="true" attribute
        ///  
        /// true of null is true 
        private bool DoesNullAttributeSayTrue()
        { 
            string attributeValue = this.reader.GetAttribute(XmlConstants.AtomNullAttributeName, XmlConstants.DataWebMetadataNamespace);
            return ((null != attributeValue) && XmlConvert.ToBoolean(attributeValue));
        }
 
        #region Tracing
 
        ///  
        /// trace Element node
        ///  
        [Conditional("TRACE")]
        private void TraceElement()
        {
            Debug.Assert(XmlNodeType.Element == this.reader.NodeType, "not positioned on Element"); 

            if (null != this.writer) 
            { 
                this.writer.Write(Util.GetWhitespaceForTracing(2 + this.reader.Depth), 0, 2 + this.reader.Depth);
                this.writer.Write("<{0}", this.reader.Name); 

                if (this.reader.MoveToFirstAttribute())
                {
                    do 
                    {
                        this.writer.Write(" {0}=\"{1}\"", this.reader.Name, this.reader.Value); 
                    } 
                    while (this.reader.MoveToNextAttribute());
 
                    this.reader.MoveToElement();
                }

                this.writer.Write(this.reader.IsEmptyElement ? " />" : ">"); 
            }
        } 
 
        /// 
        /// trace EndElement node 
        /// 
        /// indent or not
        [Conditional("TRACE")]
        private void TraceEndElement(bool indent) 
        {
            if (null != this.writer) 
            { 
                if (indent)
                { 
                    this.writer.Write(Util.GetWhitespaceForTracing(2 + this.reader.Depth), 0, 2 + this.reader.Depth);
                }

                this.writer.Write("", this.reader.Name); 
            }
        } 
 
        /// 
        /// trace string value 
        /// 
        /// value
        [Conditional("TRACE")]
        private void TraceText(string value) 
        {
            if (null != this.writer) 
            { 
                this.writer.Write(value);
            } 
        }
        #endregion

        ///  
        /// hold information about a newly constructed object until the very end of MoveNext
        ///  
        private struct EntityWithTag 
        {
            /// entity being attached 
            internal readonly object Entity;

            /// uri to edit the entity
            /// <atom:link rel="edit" href="editLink" /> 
            internal readonly Uri EditLink;
 
            /// etag of entity being attached 
            internal readonly string ETag;
 
            /// state of the entitiy
            internal readonly EntityStates State;

            /// should we actually attach the entity, i.e. false if OpenType 
            internal readonly bool AttachToContext;
 
            ///  
            /// ctor
            ///  
            /// entity
            /// editLink
            /// etag
            /// state 
            /// attach
            internal EntityWithTag(object entity, Uri editLink, string etag, EntityStates state, bool attach) 
            { 
                this.Entity = entity;
                this.EditLink = editLink; 
                this.ETag = etag;
                this.State = state;
                this.AttachToContext = attach;
            } 
        }
 
        ///  
        /// materialize objects from an application/atom+xml stream
        ///  
        /// base type of the object to create from the stream
        internal sealed class MaterializeAtomT : MaterializeAtom, IEnumerable, IEnumerator
        {
            ///  
            /// constructor
            ///  
            /// originating context 
            /// reader
            internal MaterializeAtomT(DataServiceContext context, XmlReader reader) 
                : base(context, reader, typeof(T), context.MergeOption)
            {
            }
 
            #region Current
            ///  
            /// typed current object 
            /// 
            [DebuggerHidden] 
            T IEnumerator.Current
            {
                get { return (T)this.Current; }
            } 

            #endregion 
 
            #region IEnumerable
            ///  
            /// as IEnumerable
            /// 
            /// this
            IEnumerator IEnumerable.GetEnumerator() 
            {
                this.CheckGetEnumerator(); 
                return this; 
            }
 
            #endregion
        }
    }
} 

// 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