AtomMaterializerLog.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

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

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
// Provides a class that can keep a record of changes done to the
// data service state. 
//  
//---------------------------------------------------------------------
 
namespace System.Data.Services.Client
{
    #region Namespaces.
 
    using System;
    using System.Collections; 
    using System.Collections.Generic; 
    using System.Diagnostics;
    using System.Linq; 
    using System.Reflection;
    using System.Xml;
    using System.Xml.Linq;
    using System.Text; 

    #endregion Namespaces. 
 
    /// 
    /// Use this class to keep a log of changes done by the materializer. 
    /// 
    internal class AtomMaterializerLog
    {
        #region Private fields. 

        /// Associated data context for the log. 
        private readonly DataServiceContext context; 

        /// Dictionary of identity URI to instances created during previous AppendOnly moves. 
        private readonly Dictionary appendOnlyEntries;

        /// Dictionary of identity URI to entries with media that aren't otherwise tracked.
        private readonly Dictionary foundEntriesWithMedia; 

        /// Dictionary of identity URI to tracked entities. 
        private readonly Dictionary identityStack; 

        /// List of link descriptors (data for links and state). 
        private readonly List links;

        /// Merge option used to apply changes.
        private readonly MergeOption mergeOption; 

        /// Target instance to refresh. 
        private object insertRefreshObject; 

        #endregion Private fields. 

        #region Constructors.

        ///  
        /// Initializes a new  instance.
        ///  
        /// Associated data context for the log. 
        /// Merge option used to apply changes.
        ///  
        /// Note that the merge option can't be changed.
        /// 
        internal AtomMaterializerLog(DataServiceContext context, MergeOption mergeOption)
        { 
            Debug.Assert(context != null, "context != null");
            this.appendOnlyEntries = new Dictionary(EqualityComparer.Default); 
            this.context = context; 
            this.mergeOption = mergeOption;
            this.foundEntriesWithMedia = new Dictionary(EqualityComparer.Default); 
            this.identityStack = new Dictionary(EqualityComparer.Default);
            this.links = new List();
        }
 
        #endregion Constructors.
 
        #region Internal properties. 

        /// Whether changes are being tracked. 
        internal bool Tracking
        {
            get
            { 
                return this.mergeOption != MergeOption.NoTracking;
            } 
        } 

        #endregion Internal properties. 

        #region Internal methods.

        /// Applies all accumulated changes to the associated data context. 
        /// The log should be cleared after this method successfully executed.
        internal void ApplyToContext() 
        { 
            Debug.Assert(
                this.mergeOption != MergeOption.OverwriteChanges || this.foundEntriesWithMedia.Count == 0, 
                "mergeOption != MergeOption.OverwriteChanges || foundEntriesWithMedia.Count == 0 - we only use the 'entries-with-media' lookaside when we're not in overwrite mode, otherwise we track everything through identity stack");

            if (!this.Tracking)
            { 
                return;
            } 
 
            foreach (KeyValuePair entity in this.identityStack)
            { 
                AtomEntry entry = entity.Value;
                if (entry.CreatedByMaterializer ||
                    entry.ResolvedObject == this.insertRefreshObject ||
                    entry.ShouldUpdateFromPayload) 
                {
                    // Create a new descriptor and try to attach, if one already exists, get the existing reference instead. 
                    EntityDescriptor descriptor = new EntityDescriptor(entity.Key, entry.QueryLink, entry.EditLink, entry.ResolvedObject, null, null, null, entry.ETagText, EntityStates.Unchanged); 
                    descriptor = this.context.InternalAttachEntityDescriptor(descriptor, false);
 
                    // we should always reset descriptor's state to Unchanged (old v1 behaviour)
                    descriptor.State = EntityStates.Unchanged;

                    this.ApplyMediaEntryInformation(entry, descriptor); 
                    descriptor.ServerTypeName = entry.TypeName;
                } 
                else 
                {
                    // Refresh the entity state indirectly by calling TryGetEntity. 
                    EntityStates state;
                    this.context.TryGetEntity(entity.Key, entry.ETagText, this.mergeOption, out state);
                }
            } 

            // Regardless of the merge mode, media link information should 
            // always be applied to the context. 
            foreach (AtomEntry entry in this.foundEntriesWithMedia.Values)
            { 
                Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise it wasn't found");
                EntityDescriptor descriptor = this.context.GetEntityDescriptor(entry.ResolvedObject);
                this.ApplyMediaEntryInformation(entry, descriptor);
            } 

            foreach (LinkDescriptor link in this.links) 
            { 
                if (EntityStates.Added == link.State)
                { 
                    // Added implies collection
                    if ((EntityStates.Deleted == this.context.GetEntityDescriptor(link.Target).State) ||
                        (EntityStates.Deleted == this.context.GetEntityDescriptor(link.Source).State))
                    { 
                        this.context.DeleteLink(link.Source, link.SourceProperty, link.Target);
                    } 
                    else 
                    {
                        this.context.AttachLink(link.Source, link.SourceProperty, link.Target, this.mergeOption); 
                    }
                }
                else if (EntityStates.Modified == link.State)
                { 
                    // Modified implies reference
                    object target = link.Target; 
                    if (MergeOption.PreserveChanges == this.mergeOption) 
                    {
                        LinkDescriptor end = this.context.GetLinks(link.Source, link.SourceProperty).FirstOrDefault(); 
                        if (null != end && null == end.Target)
                        {
                            // leave the SetLink(link.Source, link.SourceProperty, null)
                            continue; 
                        }
 
                        if ((null != target) && (EntityStates.Deleted == this.context.GetEntityDescriptor(target).State) || 
                            (EntityStates.Deleted == this.context.GetEntityDescriptor(link.Source).State))
                        { 
                            target = null;
                        }
                    }
 
                    this.context.AttachLink(link.Source, link.SourceProperty, target, this.mergeOption);
                } 
                else 
                {
                    // detach link 
                    Debug.Assert(EntityStates.Detached == link.State, "not detached link");
                    this.context.DetachLink(link.Source, link.SourceProperty, link.Target);
                }
            } 
        }
 
        /// Clears all state in the log. 
        internal void Clear()
        { 
            this.foundEntriesWithMedia.Clear();
            this.identityStack.Clear();
            this.links.Clear();
            this.insertRefreshObject = null; 
        }
 
        ///  
        /// Invoke this method to notify the log that an existing
        /// instance was found while resolving an object. 
        /// 
        /// Entry for instance.
        internal void FoundExistingInstance(AtomEntry entry)
        { 
            Debug.Assert(entry != null, "entry != null");
            Debug.Assert(ShouldTrackWithContext(entry), "Existing entries should be entity"); 
 
            if (this.mergeOption == MergeOption.OverwriteChanges)
            { 
                this.identityStack[entry.Identity] = entry;
            }
            else if (this.Tracking && entry.MediaLinkEntry == true)
            { 
                this.foundEntriesWithMedia[entry.Identity] = entry;
            } 
        } 

        ///  
        /// Invoke this method to notify the log that the
        /// target instance of a "directed" update was found.
        /// 
        /// Entry found. 
        /// 
        /// The target instance is typically the object that we 
        /// expect will get refreshed by the response from a POST 
        /// method.
        /// 
        /// For example if a create a Customer and POST it to
        /// a service, the response of the POST will return the
        /// re-serialized instance, with (important!) server generated
        /// values and URIs. 
        /// 
        internal void FoundTargetInstance(AtomEntry entry) 
        { 
            Debug.Assert(entry != null, "entry != null");
            Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise this is not a target"); 

            if (ShouldTrackWithContext(entry))
            {
                this.context.AttachIdentity(entry.Identity, entry.QueryLink, entry.EditLink, entry.ResolvedObject, entry.ETagText); 
                this.identityStack.Add(entry.Identity, entry);
                this.insertRefreshObject = entry.ResolvedObject; 
            } 
        }
 
        /// Attempts to resolve an entry from those tracked in the log.
        /// Entry to resolve.
        /// 
        /// After invocation, an existing entry with the same identity as 
        /// ; possibly null.
        ///  
        /// true if an existing entry was found; false otherwise. 
        internal bool TryResolve(AtomEntry entry, out AtomEntry existingEntry)
        { 
            Debug.Assert(entry != null, "entry != null");
            Debug.Assert(entry.Identity != null, "entry.Identity != null");

            if (this.identityStack.TryGetValue(entry.Identity, out existingEntry)) 
            {
                return true; 
            } 

            if (this.appendOnlyEntries.TryGetValue(entry.Identity, out existingEntry)) 
            {
                // The AppendOnly entries are valid only as long as they were not modified
                // between calls to .MoveNext().
                EntityStates state; 
                this.context.TryGetEntity(entry.Identity, entry.ETagText, this.mergeOption, out state);
                if (state == EntityStates.Unchanged) 
                { 
                    return true;
                } 
                else
                {
                    this.appendOnlyEntries.Remove(entry.Identity);
                } 
            }
 
            existingEntry = null; 
            return false;
        } 

        /// 
        /// Invoke this method to notify the log that a new link was
        /// added to a collection. 
        /// 
        ///  
        /// Instance with the collection to which  
        /// was added.
        ///  
        /// Property name for collection.
        /// Object which was added.
        internal void AddedLink(AtomEntry source, string propertyName, object target)
        { 
            Debug.Assert(source != null, "source != null");
            Debug.Assert(propertyName != null, "propertyName != null"); 
 
            if (!this.Tracking)
            { 
                return;
            }

            if (ShouldTrackWithContext(source) && ShouldTrackWithContext(target)) 
            {
                LinkDescriptor item = new LinkDescriptor(source.ResolvedObject, propertyName, target, EntityStates.Added); 
                this.links.Add(item); 
            }
        } 

        /// 
        /// Invoke this method to notify the log that a new instance
        /// was created. 
        /// 
        /// Entry for the created instance. 
        internal void CreatedInstance(AtomEntry entry) 
        {
            Debug.Assert(entry != null, "entry != null"); 
            Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise, what did we create?");
            Debug.Assert(entry.CreatedByMaterializer, "entry.CreatedByMaterializer -- otherwise we shouldn't be calling this");

            if (ShouldTrackWithContext(entry)) 
            {
                this.identityStack.Add(entry.Identity, entry); 
                if (this.mergeOption == MergeOption.AppendOnly) 
                {
                    this.appendOnlyEntries.Add(entry.Identity, entry); 
                }
            }
        }
 
        /// 
        /// Invoke this method to notify the log that a link was removed 
        /// from a collection. 
        /// 
        ///  
        /// Instance with the collection from which 
        /// was removed.
        /// 
        /// Property name for collection. 
        /// Object which was removed.
        internal void RemovedLink(AtomEntry source, string propertyName, object target) 
        { 
            Debug.Assert(source != null, "source != null");
            Debug.Assert(propertyName != null, "propertyName != null"); 

            if (ShouldTrackWithContext(source) && ShouldTrackWithContext(target))
            {
                Debug.Assert(this.Tracking, "this.Tracking -- otherwise there's an 'if' missing (it happens to be that the assert holds for all current callers"); 
                LinkDescriptor item = new LinkDescriptor(source.ResolvedObject, propertyName, target, EntityStates.Detached);
                this.links.Add(item); 
            } 
        }
 
        /// 
        /// Invoke this method to notify the log that a link was set on
        /// a property.
        ///  
        /// Entry for source object.
        /// Name of property set. 
        /// Target object. 
        internal void SetLink(AtomEntry source, string propertyName, object target)
        { 
            Debug.Assert(source != null, "source != null");
            Debug.Assert(propertyName != null, "propertyName != null");

            if (!this.Tracking) 
            {
                return; 
            } 

            if (ShouldTrackWithContext(source) && ShouldTrackWithContext(target)) 
            {
                Debug.Assert(this.Tracking, "this.Tracking -- otherwise there's an 'if' missing (it happens to be that the assert holds for all current callers");
                LinkDescriptor item = new LinkDescriptor(source.ResolvedObject, propertyName, target, EntityStates.Modified);
                this.links.Add(item); 
            }
        } 
 
        #endregion Internal methods.
 
        #region Private methods.

        /// 
        /// Returns true if we should track this entry with context 
        /// 
        /// The atom entry 
        /// true if entry should be tracked 
        private static bool ShouldTrackWithContext(AtomEntry entry)
        { 
            Debug.Assert(entry.ActualType != null, "Entry with no type added to log");
            return entry.ActualType.IsEntityType;
        }
 
        /// 
        /// Returns true if we should track this entity with context 
        ///  
        /// The resolved instance
        /// true if entry should be tracked 
        private static bool ShouldTrackWithContext(object entity)
        {
            if (entity == null)
            { 
                // you can set link to null, we need to track these values
                return true; 
            } 

            ClientType type = ClientType.Create(entity.GetType()); 
            return type.IsEntityType;
        }

        ///  
        /// Applies the media entry information (if any) to the specified
        /// . 
        ///  
        /// Entry with (potential) media entry information to apply.
        /// Descriptor to update. 
        private void ApplyMediaEntryInformation(AtomEntry entry, EntityDescriptor descriptor)
        {
            Debug.Assert(entry != null, "entry != null");
            Debug.Assert(descriptor != null, "descriptor != null"); 

            if (entry.MediaEditUri != null || entry.MediaContentUri != null) 
            { 
                //
 
                if (entry.MediaEditUri != null)
                {
                    descriptor.EditStreamUri = new Uri(this.context.BaseUriWithSlash, entry.MediaEditUri);
                } 

                if (entry.MediaContentUri != null) 
                { 
                    descriptor.ReadStreamUri = new Uri(this.context.BaseUriWithSlash, entry.MediaContentUri);
                } 

                descriptor.StreamETag = entry.StreamETagText;
            }
        } 

        #endregion Private methods. 
    } 
}

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