MaterializeFromAtom.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataWeb / Client / System / Data / Services / Client / MaterializeFromAtom.cs / 1305376 / MaterializeFromAtom.cs

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

namespace System.Data.Services.Client 
{
    #region Namespaces.

    using System; 
    using System.Collections;
    using System.Collections.Generic; 
    using System.Diagnostics; 
    using System.Linq;
    using System.Xml; 
    using System.Xml.Linq;
    using System.Text;
    using System.Linq.Expressions;
 
    #endregion Namespaces.
 
    ///  
    /// Use this class to 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; 

        #region Private fields. 
 
        /// Row count value representing the initial state (-1)
        private const long CountStateInitial = -1; 

        /// Row count value representing the failure state (-2)
        private const long CountStateFailure = -2;
 
        /// Options when deserializing properties to the target type.
        private readonly bool ignoreMissingProperties; 
 
        /// Backreference to the context to allow type resolution.
        private readonly DataServiceContext context; 

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

        /// Materializer from which instances are read. 
        /// 
        /// The log for the materializer stores all changes for the
        /// associated data context.
        ///  
        private readonly AtomMaterializer materializer;
 
        /// Parser used by materializer. 
        /// Odd thing to have, only here to force a lookahead for inline elements.
        private readonly AtomParser parser; 

        /// source reader (may be running on top of a buffer or be the real reader)
        private XmlReader reader;
 
        /// untyped current object
        private object current; 
 
        /// has GetEnumerator been called?
        private bool calledGetEnumerator; 

        /// The count tag's value, if requested
        private long countValue;
 
        /// Whether anything has been read.
        private bool moved; 
 
        /// 
        /// 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
 
        #endregion Private fields.

        /// 
        /// constructor 
        /// 
        /// originating context 
        /// reader 
        /// Query components (projection, expected type)
        /// Projection plan (if compiled in an earlier query). 
        /// merge option to use for this materialization pass
        internal MaterializeAtom(DataServiceContext context, XmlReader reader, QueryComponents queryComponents, ProjectionPlan plan, MergeOption mergeOption)
        {
            Debug.Assert(queryComponents != null, "queryComponents != null"); 

            this.context = context; 
            this.elementType = queryComponents.LastSegmentType; 
            this.MergeOptionValue = mergeOption;
            this.ignoreMissingProperties = context.IgnoreMissingProperties; 
            this.reader = (reader == null) ? null : new System.Data.Services.Client.Xml.XmlAtomErrorReader(reader);
            this.countValue = CountStateInitial;
            this.expectingSingleValue = ClientConvert.IsKnownNullableType(elementType);
 
            Debug.Assert(reader != null, "Materializer reader is null! Did you mean to use Materializer.ResultsWrapper/EmptyResults?");
 
            // NOTE: dataNamespace is used for reference equality, and while it looks like 
            // a variable, it appears that it will only get set to XmlConstants.DataWebNamespace
            // at runtime. Therefore we remove string dataNamespace as a field here. 
            // this.dataNamespace = reader != null ? reader.Settings.NameTable.Add(context.DataNamespace) : null;
            reader.Settings.NameTable.Add(context.DataNamespace);

            string typeScheme = this.context.TypeScheme.OriginalString; 
            this.parser = new AtomParser(this.reader, AtomParser.XElementBuilderCallback, typeScheme, context.DataNamespace);
            AtomMaterializerLog log = new AtomMaterializerLog(this.context, mergeOption); 
            Type implementationType; 
            Type materializerType = GetTypeForMaterializer(this.expectingSingleValue, this.elementType, out implementationType);
            this.materializer = new AtomMaterializer(parser, context, materializerType, this.ignoreMissingProperties, mergeOption, log, this.MaterializedObjectCallback, queryComponents, plan); 
        }

        private void MaterializedObjectCallback(object tag, object entity)
        { 
            Debug.Assert(tag != null, "tag != null");
            Debug.Assert(entity != null, "entity != null"); 
 
            XElement data = (XElement)tag;
            if (this.context.HasReadingEntityHandlers) 
            {
                XmlUtil.RemoveDuplicateNamespaceAttributes(data);
                this.context.FireReadingEntityEvent(entity, data);
            } 
        }
 
        ///  
        /// Private internal constructor used for creating empty wrapper.
        ///  
        private MaterializeAtom()
        {
        }
 
        /// 
        /// DO NOT USE - this is a private hook for client unit tests to construct materializers 
        ///  
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        private MaterializeAtom(DataServiceContext context, XmlReader reader, Type type, MergeOption mergeOption) 
            : this(context, reader, new QueryComponents(null, Util.DataServiceVersionEmpty, type, null, null), null, mergeOption)
        {
        }
 
        #region Current
 
        /// Loosely typed current object property. 
        /// 
        /// The value should be of the right type, as the materializer takes the 
        /// expected type into account.
        /// 
        public object Current
        { 
            get
            { 
                object currentValue = this.current; 
                return currentValue;
            } 
        }

        #endregion
 
        /// 
        /// A materializer for empty results 
        ///  
        internal static MaterializeAtom EmptyResults
        { 
            get
            {
                return new ResultsWrapper(null, null);
            } 
        }
 
        ///  
        /// Returns true, if the materialize atom has empty results, because of
        /// IgnoreResourceNotFoundException flag set in context 
        /// 
        internal bool IsEmptyResults
        {
            get { return this.reader == null; } 
        }
 
        ///  
        /// The context object this materializer belongs to
        ///  
        internal DataServiceContext Context
        {
            get { return this.context; }
        } 

        #region IDisposable 
        ///  
        /// dispose
        ///  
        public void Dispose()
        {
            this.current = null;
 
            if (null != this.reader)
            { 
                ((IDisposable)this.reader).Dispose(); 
            }
 
            if (null != this.writer)
            {
                this.writer.Dispose();
            } 

            GC.SuppressFinalize(this); 
        } 

        #endregion 

        #region IEnumerable
        /// 
        /// as IEnumerable 
        /// 
        /// this 
        public virtual IEnumerator GetEnumerator() 
        {
            this.CheckGetEnumerator(); 
            return this;
        }
        #endregion
 
        private static Type GetTypeForMaterializer(bool expectingSingleValue, Type elementType, out Type implementationType)
        { 
            if (!expectingSingleValue && typeof(IEnumerable).IsAssignableFrom(elementType)) 
            {
                implementationType = ClientType.GetImplementationType(elementType, typeof(ICollection<>)); 
                if (implementationType != null)
                {
                    Type expectedType = implementationType.GetGenericArguments()[0]; // already know its IList<>
                    return expectedType; 
                }
            } 
 
            implementationType = null;
            return elementType; 
        }

        /// 
        /// Creates the next object from the stream. 
        /// 
        /// false if stream is finished 
        public bool MoveNext() 
        {
            bool applying = this.context.ApplyingChanges; 
            try
            {
                this.context.ApplyingChanges = true;
                return this.MoveNextInternal(); 
            }
            finally 
            { 
                this.context.ApplyingChanges = applying;
            } 
        }

        /// 
        /// Creates the next object from the stream. 
        /// 
        /// false if stream is finished 
        private bool MoveNextInternal() 
        {
            // For empty results, just return false. 
            if (this.reader == null)
            {
                Debug.Assert(this.current == null, "this.current == null -- otherwise this.reader should have some value.");
                return false; 
            }
 
            this.current = null; 
            this.materializer.Log.Clear();
 
            bool result = false;
            Type implementationType;
            GetTypeForMaterializer(this.expectingSingleValue, this.elementType, out implementationType);
            if (implementationType != null) 
            {
                if (this.moved) 
                { 
                    return false;
                } 

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

                while (this.materializer.Read())
                {
                    this.moved = true; 
                    list.Add(this.materializer.CurrentValue);
                } 
 
                this.current = list;
                result = true; 
            }

            if (null == this.current)
            { 
                if (this.expectingSingleValue && this.moved)
                { 
                    result = false; 
                }
                else 
                {
                    result = this.materializer.Read();
                    if (result)
                    { 
                        this.current = this.materializer.CurrentValue;
                    } 
 
                    this.moved = true;
                } 
            }

            this.materializer.Log.ApplyToContext();
 
            return result;
        } 
 
        /// 
        /// Not supported. 
        /// 
        /// Always thrown
        void System.Collections.IEnumerator.Reset()
        { 
            throw Error.NotSupported();
        } 
 
        /// 
        ///  Creates materializer for results 
        /// 
        /// the results to wrap
        /// a new materializer
        internal static MaterializeAtom CreateWrapper(IEnumerable results) 
        {
            return new ResultsWrapper(results, null); 
        } 

        /// Creates a materializer for partial result sets. 
        /// The current page of results
        /// The continuation for the results.
        /// A new materializer.
        internal static MaterializeAtom CreateWrapper(IEnumerable results, DataServiceQueryContinuation continuation) 
        {
            return new ResultsWrapper(results, continuation); 
        } 

        /// set the inserted object expected in the response 
        /// object being inserted that the response is looking for
        internal void SetInsertingObject(object addedObject)
        {
            this.materializer.TargetInstance = addedObject; 
        }
 
        internal static void SkipToEnd(XmlReader reader) 
        {
            Debug.Assert(reader != null, "reader != null"); 
            Debug.Assert(reader.NodeType == XmlNodeType.Element, "reader.NodeType == XmlNodeType.Element");

            if (reader.IsEmptyElement)
            { 
                return;
            } 
 
            int readerDepth = reader.Depth;
            while (reader.Read()) 
            {
                if (reader.NodeType == XmlNodeType.EndElement && reader.Depth == readerDepth)
                {
                    return; 
                }
            } 
        } 

        ///  
        /// The count tag's value, if requested
        /// 
        /// The count value returned from the server
        internal long CountValue() 
        {
            if (this.countValue == CountStateInitial) 
            { 
                this.ReadCountValue();
            } 
            else if (this.countValue == CountStateFailure)
            {
                throw new InvalidOperationException(Strings.MaterializeFromAtom_CountNotPresent);
            } 

            return this.countValue; 
        } 

        ///  
        /// Returns the next link URI for the collection key
        /// 
        /// The collection for which the Uri is returned, or null, if the top level link is to be returned
        /// An Uri pointing to the next page for the collection 
        internal virtual DataServiceQueryContinuation GetContinuation(IEnumerable key)
        { 
            Debug.Assert(this.materializer != null, "Materializer is null!"); 

            DataServiceQueryContinuation result; 
            if (key == null)
            {
                if ((this.expectingSingleValue && !this.moved) || (!this.expectingSingleValue && !this.materializer.IsEndOfStream))
                { 
                    // expectingSingleValue && !moved : haven't started parsing single value (single value should not have next link anyway)
                    // !expectingSingleValue && !IsEndOfStream : collection type feed did not finish parsing yet 
                    throw new InvalidOperationException(Strings.MaterializeFromAtom_TopLevelLinkNotAvailable); 
                }
 
                // we have already moved to the end of stream
                // are we singleton or just an entry?
                if (this.expectingSingleValue || this.materializer.CurrentFeed == null)
                { 
                    result = null;
                } 
                else 
                {
                    // DEVNOTE([....]): The next link uri should never be edited by the client, and therefore it must be absolute 
                    result = DataServiceQueryContinuation.Create(
                        this.materializer.CurrentFeed.NextLink,
                        this.materializer.MaterializeEntryPlan);
                } 
            }
            else 
            { 
                if (!this.materializer.NextLinkTable.TryGetValue(key, out result))
                { 
                    // someone has asked for a collection that's "out of scope" or doesn't exist
                    throw new ArgumentException(Strings.MaterializeFromAtom_CollectionKeyNotPresentInLinkTable);
                }
            } 

            return result; 
        } 

        /// verify the GetEnumerator can only be called once 
        private void CheckGetEnumerator()
        {
            if (this.calledGetEnumerator)
            { 
                throw Error.NotSupported(Strings.Deserialize_GetEnumerator);
            } 
 
            this.calledGetEnumerator = true;
        } 

        /// 
        /// read the m2:count tag in the feed
        ///  
        private void ReadCountValue()
        { 
            Debug.Assert(this.countValue == CountStateInitial, "Count value is not in the initial state"); 

            if (this.materializer.CurrentFeed != null && 
                this.materializer.CurrentFeed.Count.HasValue)
            {
                this.countValue = this.materializer.CurrentFeed.Count.Value;
                return; 
            }
 
            // find the first element tag 
            while (this.reader.NodeType != XmlNodeType.Element && this.reader.Read())
            { 
            }

            if (this.reader.EOF)
            { 
                throw new InvalidOperationException(Strings.MaterializeFromAtom_CountNotPresent);
            } 
 
            // the tag Should only be  or  tag:
            Debug.Assert( 
                (Util.AreSame(XmlConstants.AtomNamespace, this.reader.NamespaceURI) &&
                Util.AreSame(XmlConstants.AtomFeedElementName, this.reader.LocalName)) ||
                (Util.AreSame(XmlConstants.DataWebNamespace, this.reader.NamespaceURI) &&
                Util.AreSame(XmlConstants.LinkCollectionElementName, this.reader.LocalName)), 
                " or  tag expected");
 
            // Create the XElement for look-ahead 
            // DEVNOTE([....]):
            // This is not streaming friendly! 
            XElement element = XElement.Load(this.reader);
            this.reader.Close();

            // Read the count value from the xelement 
            XElement countNode = element.Descendants(XNamespace.Get(XmlConstants.DataWebMetadataNamespace) + XmlConstants.RowCountElement).FirstOrDefault();
 
            if (countNode == null) 
            {
                throw new InvalidOperationException(Strings.MaterializeFromAtom_CountNotPresent); 
            }
            else
            {
                if (!long.TryParse(countNode.Value, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out this.countValue)) 
                {
                    throw new FormatException(Strings.MaterializeFromAtom_CountFormatError); 
                } 

                if (this.countValue < 0) 
                {
                    throw new FormatException(Strings.MaterializeFromAtom_CountFormatError);
                }
            } 

            this.reader = new System.Data.Services.Client.Xml.XmlAtomErrorReader(element.CreateReader()); 
            this.parser.ReplaceReader(this.reader); 
        }
 
        /// 
        /// Gets the  for the (potentially
        /// user-callback-resolved) type name.
        ///  
        /// String with type name to check.
        /// Context in which type is being resolved. 
        /// Expected type for the name. 
        /// 
        /// Whether to check that a user-overriden type is assignable to a 
        /// variable of type .
        /// 
        /// The  for the given type name.
        internal static ClientType GetEntryClientType(string typeName, DataServiceContext context, Type expectedType, bool checkAssignable) 
        {
            Debug.Assert(context != null, "context != null"); 
            Type resolvedType = context.ResolveTypeFromName(typeName, expectedType, checkAssignable); 
            ClientType result = ClientType.Create(resolvedType);
            Debug.Assert(result != null, "result != null -- otherwise ClientType.Create returned null"); 
            return result;
        }

        internal static string ReadElementString(XmlReader reader, bool checkNullAttribute) 
        {
            Debug.Assert(reader != null, "reader != null"); 
            Debug.Assert( 
                reader.NodeType == XmlNodeType.Element,
                "reader.NodeType == XmlNodeType.Element -- otherwise caller is confused as to where the reader is"); 

            string result = null;
            bool empty = checkNullAttribute && !Util.DoesNullAttributeSayTrue(reader);
 
            if (reader.IsEmptyElement)
            { 
                return (empty ? String.Empty : null); 
            }
 
            while (reader.Read())
            {
                switch (reader.NodeType)
                { 
                    case XmlNodeType.EndElement:
                        return result ?? (empty ? String.Empty : null); 
                    case XmlNodeType.CDATA: 
                    case XmlNodeType.Text:
                    case XmlNodeType.SignificantWhitespace: 
                        if (null != result)
                        {
                            throw Error.InvalidOperation(Strings.Deserialize_MixedTextWithComment);
                        } 

                        result = reader.Value; 
                        break; 
                    case XmlNodeType.Comment:
                    case XmlNodeType.Whitespace: 
                        break;

                    #region XmlNodeType error
                    case XmlNodeType.Element: 
                        goto default;
 
                    default: 
                        throw Error.InvalidOperation(Strings.Deserialize_ExpectingSimpleValue);
                    #endregion 
                }
            }

            // xml ended before EndElement? 
            throw Error.InvalidOperation(Strings.Deserialize_ExpectingSimpleValue);
        } 
 
        /// 
        /// Private type to wrap partial (paged) results and make it look like standard materialized results. 
        /// 
        private class ResultsWrapper : MaterializeAtom
        {
            #region Private fields. 

            ///  The results to wrap  
            private readonly IEnumerable results; 

            /// A continuation to the next page of results. 
            private readonly DataServiceQueryContinuation continuation;

            #endregion Private fields.
 
            /// 
            /// Creates a wrapper for raw results 
            ///  
            /// the results to wrap
            /// The continuation for this query. 
            internal ResultsWrapper(IEnumerable results, DataServiceQueryContinuation continuation)
            {
                this.results = results ?? new object[0];
                this.continuation = continuation; 
            }
 
            ///  
            /// Get the next link to the result set
            ///  
            /// When equals to null, returns the next link associated with this collection. Otherwise throws InvalidOperationException.
            /// The continuation for this query.
            internal override DataServiceQueryContinuation GetContinuation(IEnumerable key)
            { 
                if (key == null)
                { 
                    return this.continuation; 
                }
                else 
                {
                    throw new InvalidOperationException(Strings.MaterializeFromAtom_GetNestLinkForFlatCollection);
                }
            } 

            ///  
            /// Gets Enumerator for wrapped results. 
            /// 
            /// IEnumerator for results 
            public override IEnumerator GetEnumerator()
            {
                return this.results.GetEnumerator();
            } 
        }
    } 
} 

// 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 
{
    #region Namespaces.

    using System; 
    using System.Collections;
    using System.Collections.Generic; 
    using System.Diagnostics; 
    using System.Linq;
    using System.Xml; 
    using System.Xml.Linq;
    using System.Text;
    using System.Linq.Expressions;
 
    #endregion Namespaces.
 
    ///  
    /// Use this class to 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; 

        #region Private fields. 
 
        /// Row count value representing the initial state (-1)
        private const long CountStateInitial = -1; 

        /// Row count value representing the failure state (-2)
        private const long CountStateFailure = -2;
 
        /// Options when deserializing properties to the target type.
        private readonly bool ignoreMissingProperties; 
 
        /// Backreference to the context to allow type resolution.
        private readonly DataServiceContext context; 

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

        /// Materializer from which instances are read. 
        /// 
        /// The log for the materializer stores all changes for the
        /// associated data context.
        ///  
        private readonly AtomMaterializer materializer;
 
        /// Parser used by materializer. 
        /// Odd thing to have, only here to force a lookahead for inline elements.
        private readonly AtomParser parser; 

        /// source reader (may be running on top of a buffer or be the real reader)
        private XmlReader reader;
 
        /// untyped current object
        private object current; 
 
        /// has GetEnumerator been called?
        private bool calledGetEnumerator; 

        /// The count tag's value, if requested
        private long countValue;
 
        /// Whether anything has been read.
        private bool moved; 
 
        /// 
        /// 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
 
        #endregion Private fields.

        /// 
        /// constructor 
        /// 
        /// originating context 
        /// reader 
        /// Query components (projection, expected type)
        /// Projection plan (if compiled in an earlier query). 
        /// merge option to use for this materialization pass
        internal MaterializeAtom(DataServiceContext context, XmlReader reader, QueryComponents queryComponents, ProjectionPlan plan, MergeOption mergeOption)
        {
            Debug.Assert(queryComponents != null, "queryComponents != null"); 

            this.context = context; 
            this.elementType = queryComponents.LastSegmentType; 
            this.MergeOptionValue = mergeOption;
            this.ignoreMissingProperties = context.IgnoreMissingProperties; 
            this.reader = (reader == null) ? null : new System.Data.Services.Client.Xml.XmlAtomErrorReader(reader);
            this.countValue = CountStateInitial;
            this.expectingSingleValue = ClientConvert.IsKnownNullableType(elementType);
 
            Debug.Assert(reader != null, "Materializer reader is null! Did you mean to use Materializer.ResultsWrapper/EmptyResults?");
 
            // NOTE: dataNamespace is used for reference equality, and while it looks like 
            // a variable, it appears that it will only get set to XmlConstants.DataWebNamespace
            // at runtime. Therefore we remove string dataNamespace as a field here. 
            // this.dataNamespace = reader != null ? reader.Settings.NameTable.Add(context.DataNamespace) : null;
            reader.Settings.NameTable.Add(context.DataNamespace);

            string typeScheme = this.context.TypeScheme.OriginalString; 
            this.parser = new AtomParser(this.reader, AtomParser.XElementBuilderCallback, typeScheme, context.DataNamespace);
            AtomMaterializerLog log = new AtomMaterializerLog(this.context, mergeOption); 
            Type implementationType; 
            Type materializerType = GetTypeForMaterializer(this.expectingSingleValue, this.elementType, out implementationType);
            this.materializer = new AtomMaterializer(parser, context, materializerType, this.ignoreMissingProperties, mergeOption, log, this.MaterializedObjectCallback, queryComponents, plan); 
        }

        private void MaterializedObjectCallback(object tag, object entity)
        { 
            Debug.Assert(tag != null, "tag != null");
            Debug.Assert(entity != null, "entity != null"); 
 
            XElement data = (XElement)tag;
            if (this.context.HasReadingEntityHandlers) 
            {
                XmlUtil.RemoveDuplicateNamespaceAttributes(data);
                this.context.FireReadingEntityEvent(entity, data);
            } 
        }
 
        ///  
        /// Private internal constructor used for creating empty wrapper.
        ///  
        private MaterializeAtom()
        {
        }
 
        /// 
        /// DO NOT USE - this is a private hook for client unit tests to construct materializers 
        ///  
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        private MaterializeAtom(DataServiceContext context, XmlReader reader, Type type, MergeOption mergeOption) 
            : this(context, reader, new QueryComponents(null, Util.DataServiceVersionEmpty, type, null, null), null, mergeOption)
        {
        }
 
        #region Current
 
        /// Loosely typed current object property. 
        /// 
        /// The value should be of the right type, as the materializer takes the 
        /// expected type into account.
        /// 
        public object Current
        { 
            get
            { 
                object currentValue = this.current; 
                return currentValue;
            } 
        }

        #endregion
 
        /// 
        /// A materializer for empty results 
        ///  
        internal static MaterializeAtom EmptyResults
        { 
            get
            {
                return new ResultsWrapper(null, null);
            } 
        }
 
        ///  
        /// Returns true, if the materialize atom has empty results, because of
        /// IgnoreResourceNotFoundException flag set in context 
        /// 
        internal bool IsEmptyResults
        {
            get { return this.reader == null; } 
        }
 
        ///  
        /// The context object this materializer belongs to
        ///  
        internal DataServiceContext Context
        {
            get { return this.context; }
        } 

        #region IDisposable 
        ///  
        /// dispose
        ///  
        public void Dispose()
        {
            this.current = null;
 
            if (null != this.reader)
            { 
                ((IDisposable)this.reader).Dispose(); 
            }
 
            if (null != this.writer)
            {
                this.writer.Dispose();
            } 

            GC.SuppressFinalize(this); 
        } 

        #endregion 

        #region IEnumerable
        /// 
        /// as IEnumerable 
        /// 
        /// this 
        public virtual IEnumerator GetEnumerator() 
        {
            this.CheckGetEnumerator(); 
            return this;
        }
        #endregion
 
        private static Type GetTypeForMaterializer(bool expectingSingleValue, Type elementType, out Type implementationType)
        { 
            if (!expectingSingleValue && typeof(IEnumerable).IsAssignableFrom(elementType)) 
            {
                implementationType = ClientType.GetImplementationType(elementType, typeof(ICollection<>)); 
                if (implementationType != null)
                {
                    Type expectedType = implementationType.GetGenericArguments()[0]; // already know its IList<>
                    return expectedType; 
                }
            } 
 
            implementationType = null;
            return elementType; 
        }

        /// 
        /// Creates the next object from the stream. 
        /// 
        /// false if stream is finished 
        public bool MoveNext() 
        {
            bool applying = this.context.ApplyingChanges; 
            try
            {
                this.context.ApplyingChanges = true;
                return this.MoveNextInternal(); 
            }
            finally 
            { 
                this.context.ApplyingChanges = applying;
            } 
        }

        /// 
        /// Creates the next object from the stream. 
        /// 
        /// false if stream is finished 
        private bool MoveNextInternal() 
        {
            // For empty results, just return false. 
            if (this.reader == null)
            {
                Debug.Assert(this.current == null, "this.current == null -- otherwise this.reader should have some value.");
                return false; 
            }
 
            this.current = null; 
            this.materializer.Log.Clear();
 
            bool result = false;
            Type implementationType;
            GetTypeForMaterializer(this.expectingSingleValue, this.elementType, out implementationType);
            if (implementationType != null) 
            {
                if (this.moved) 
                { 
                    return false;
                } 

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

                while (this.materializer.Read())
                {
                    this.moved = true; 
                    list.Add(this.materializer.CurrentValue);
                } 
 
                this.current = list;
                result = true; 
            }

            if (null == this.current)
            { 
                if (this.expectingSingleValue && this.moved)
                { 
                    result = false; 
                }
                else 
                {
                    result = this.materializer.Read();
                    if (result)
                    { 
                        this.current = this.materializer.CurrentValue;
                    } 
 
                    this.moved = true;
                } 
            }

            this.materializer.Log.ApplyToContext();
 
            return result;
        } 
 
        /// 
        /// Not supported. 
        /// 
        /// Always thrown
        void System.Collections.IEnumerator.Reset()
        { 
            throw Error.NotSupported();
        } 
 
        /// 
        ///  Creates materializer for results 
        /// 
        /// the results to wrap
        /// a new materializer
        internal static MaterializeAtom CreateWrapper(IEnumerable results) 
        {
            return new ResultsWrapper(results, null); 
        } 

        /// Creates a materializer for partial result sets. 
        /// The current page of results
        /// The continuation for the results.
        /// A new materializer.
        internal static MaterializeAtom CreateWrapper(IEnumerable results, DataServiceQueryContinuation continuation) 
        {
            return new ResultsWrapper(results, continuation); 
        } 

        /// set the inserted object expected in the response 
        /// object being inserted that the response is looking for
        internal void SetInsertingObject(object addedObject)
        {
            this.materializer.TargetInstance = addedObject; 
        }
 
        internal static void SkipToEnd(XmlReader reader) 
        {
            Debug.Assert(reader != null, "reader != null"); 
            Debug.Assert(reader.NodeType == XmlNodeType.Element, "reader.NodeType == XmlNodeType.Element");

            if (reader.IsEmptyElement)
            { 
                return;
            } 
 
            int readerDepth = reader.Depth;
            while (reader.Read()) 
            {
                if (reader.NodeType == XmlNodeType.EndElement && reader.Depth == readerDepth)
                {
                    return; 
                }
            } 
        } 

        ///  
        /// The count tag's value, if requested
        /// 
        /// The count value returned from the server
        internal long CountValue() 
        {
            if (this.countValue == CountStateInitial) 
            { 
                this.ReadCountValue();
            } 
            else if (this.countValue == CountStateFailure)
            {
                throw new InvalidOperationException(Strings.MaterializeFromAtom_CountNotPresent);
            } 

            return this.countValue; 
        } 

        ///  
        /// Returns the next link URI for the collection key
        /// 
        /// The collection for which the Uri is returned, or null, if the top level link is to be returned
        /// An Uri pointing to the next page for the collection 
        internal virtual DataServiceQueryContinuation GetContinuation(IEnumerable key)
        { 
            Debug.Assert(this.materializer != null, "Materializer is null!"); 

            DataServiceQueryContinuation result; 
            if (key == null)
            {
                if ((this.expectingSingleValue && !this.moved) || (!this.expectingSingleValue && !this.materializer.IsEndOfStream))
                { 
                    // expectingSingleValue && !moved : haven't started parsing single value (single value should not have next link anyway)
                    // !expectingSingleValue && !IsEndOfStream : collection type feed did not finish parsing yet 
                    throw new InvalidOperationException(Strings.MaterializeFromAtom_TopLevelLinkNotAvailable); 
                }
 
                // we have already moved to the end of stream
                // are we singleton or just an entry?
                if (this.expectingSingleValue || this.materializer.CurrentFeed == null)
                { 
                    result = null;
                } 
                else 
                {
                    // DEVNOTE([....]): The next link uri should never be edited by the client, and therefore it must be absolute 
                    result = DataServiceQueryContinuation.Create(
                        this.materializer.CurrentFeed.NextLink,
                        this.materializer.MaterializeEntryPlan);
                } 
            }
            else 
            { 
                if (!this.materializer.NextLinkTable.TryGetValue(key, out result))
                { 
                    // someone has asked for a collection that's "out of scope" or doesn't exist
                    throw new ArgumentException(Strings.MaterializeFromAtom_CollectionKeyNotPresentInLinkTable);
                }
            } 

            return result; 
        } 

        /// verify the GetEnumerator can only be called once 
        private void CheckGetEnumerator()
        {
            if (this.calledGetEnumerator)
            { 
                throw Error.NotSupported(Strings.Deserialize_GetEnumerator);
            } 
 
            this.calledGetEnumerator = true;
        } 

        /// 
        /// read the m2:count tag in the feed
        ///  
        private void ReadCountValue()
        { 
            Debug.Assert(this.countValue == CountStateInitial, "Count value is not in the initial state"); 

            if (this.materializer.CurrentFeed != null && 
                this.materializer.CurrentFeed.Count.HasValue)
            {
                this.countValue = this.materializer.CurrentFeed.Count.Value;
                return; 
            }
 
            // find the first element tag 
            while (this.reader.NodeType != XmlNodeType.Element && this.reader.Read())
            { 
            }

            if (this.reader.EOF)
            { 
                throw new InvalidOperationException(Strings.MaterializeFromAtom_CountNotPresent);
            } 
 
            // the tag Should only be  or  tag:
            Debug.Assert( 
                (Util.AreSame(XmlConstants.AtomNamespace, this.reader.NamespaceURI) &&
                Util.AreSame(XmlConstants.AtomFeedElementName, this.reader.LocalName)) ||
                (Util.AreSame(XmlConstants.DataWebNamespace, this.reader.NamespaceURI) &&
                Util.AreSame(XmlConstants.LinkCollectionElementName, this.reader.LocalName)), 
                " or  tag expected");
 
            // Create the XElement for look-ahead 
            // DEVNOTE([....]):
            // This is not streaming friendly! 
            XElement element = XElement.Load(this.reader);
            this.reader.Close();

            // Read the count value from the xelement 
            XElement countNode = element.Descendants(XNamespace.Get(XmlConstants.DataWebMetadataNamespace) + XmlConstants.RowCountElement).FirstOrDefault();
 
            if (countNode == null) 
            {
                throw new InvalidOperationException(Strings.MaterializeFromAtom_CountNotPresent); 
            }
            else
            {
                if (!long.TryParse(countNode.Value, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out this.countValue)) 
                {
                    throw new FormatException(Strings.MaterializeFromAtom_CountFormatError); 
                } 

                if (this.countValue < 0) 
                {
                    throw new FormatException(Strings.MaterializeFromAtom_CountFormatError);
                }
            } 

            this.reader = new System.Data.Services.Client.Xml.XmlAtomErrorReader(element.CreateReader()); 
            this.parser.ReplaceReader(this.reader); 
        }
 
        /// 
        /// Gets the  for the (potentially
        /// user-callback-resolved) type name.
        ///  
        /// String with type name to check.
        /// Context in which type is being resolved. 
        /// Expected type for the name. 
        /// 
        /// Whether to check that a user-overriden type is assignable to a 
        /// variable of type .
        /// 
        /// The  for the given type name.
        internal static ClientType GetEntryClientType(string typeName, DataServiceContext context, Type expectedType, bool checkAssignable) 
        {
            Debug.Assert(context != null, "context != null"); 
            Type resolvedType = context.ResolveTypeFromName(typeName, expectedType, checkAssignable); 
            ClientType result = ClientType.Create(resolvedType);
            Debug.Assert(result != null, "result != null -- otherwise ClientType.Create returned null"); 
            return result;
        }

        internal static string ReadElementString(XmlReader reader, bool checkNullAttribute) 
        {
            Debug.Assert(reader != null, "reader != null"); 
            Debug.Assert( 
                reader.NodeType == XmlNodeType.Element,
                "reader.NodeType == XmlNodeType.Element -- otherwise caller is confused as to where the reader is"); 

            string result = null;
            bool empty = checkNullAttribute && !Util.DoesNullAttributeSayTrue(reader);
 
            if (reader.IsEmptyElement)
            { 
                return (empty ? String.Empty : null); 
            }
 
            while (reader.Read())
            {
                switch (reader.NodeType)
                { 
                    case XmlNodeType.EndElement:
                        return result ?? (empty ? String.Empty : null); 
                    case XmlNodeType.CDATA: 
                    case XmlNodeType.Text:
                    case XmlNodeType.SignificantWhitespace: 
                        if (null != result)
                        {
                            throw Error.InvalidOperation(Strings.Deserialize_MixedTextWithComment);
                        } 

                        result = reader.Value; 
                        break; 
                    case XmlNodeType.Comment:
                    case XmlNodeType.Whitespace: 
                        break;

                    #region XmlNodeType error
                    case XmlNodeType.Element: 
                        goto default;
 
                    default: 
                        throw Error.InvalidOperation(Strings.Deserialize_ExpectingSimpleValue);
                    #endregion 
                }
            }

            // xml ended before EndElement? 
            throw Error.InvalidOperation(Strings.Deserialize_ExpectingSimpleValue);
        } 
 
        /// 
        /// Private type to wrap partial (paged) results and make it look like standard materialized results. 
        /// 
        private class ResultsWrapper : MaterializeAtom
        {
            #region Private fields. 

            ///  The results to wrap  
            private readonly IEnumerable results; 

            /// A continuation to the next page of results. 
            private readonly DataServiceQueryContinuation continuation;

            #endregion Private fields.
 
            /// 
            /// Creates a wrapper for raw results 
            ///  
            /// the results to wrap
            /// The continuation for this query. 
            internal ResultsWrapper(IEnumerable results, DataServiceQueryContinuation continuation)
            {
                this.results = results ?? new object[0];
                this.continuation = continuation; 
            }
 
            ///  
            /// Get the next link to the result set
            ///  
            /// When equals to null, returns the next link associated with this collection. Otherwise throws InvalidOperationException.
            /// The continuation for this query.
            internal override DataServiceQueryContinuation GetContinuation(IEnumerable key)
            { 
                if (key == null)
                { 
                    return this.continuation; 
                }
                else 
                {
                    throw new InvalidOperationException(Strings.MaterializeFromAtom_GetNestLinkForFlatCollection);
                }
            } 

            ///  
            /// Gets Enumerator for wrapped results. 
            /// 
            /// IEnumerator for results 
            public override IEnumerator GetEnumerator()
            {
                return this.results.GetEnumerator();
            } 
        }
    } 
} 

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