ChangeTracker.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 / DLinq / Dlinq / ChangeTracker.cs / 1305376 / ChangeTracker.cs

                            using System; 
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; 
using System.Reflection;
using System.Text; 
 
namespace System.Data.Linq {
    using System.Data.Linq.Mapping; 
    using System.Data.Linq.Provider;

    internal abstract class ChangeTracker {
        ///  
        /// Starts tracking an object as 'unchanged'
        ///  
        ///  
        /// 
        internal abstract TrackedObject Track(object obj); 
        /// 
        /// Starts tracking an object as 'unchanged', and optionally
        /// 'weakly' tracks all other referenced objects recursively.
        ///  
        /// 
        /// True if all untracked objects in the graph 
        /// should be tracked recursively. 
        /// 
        internal abstract TrackedObject Track(object obj, bool recurse); 
        /// 
        /// Fast-tracks an object that is already in identity cache
        /// 
        ///  
        internal abstract void FastTrack(object obj);
        internal abstract bool IsTracked(object obj); 
        internal abstract TrackedObject GetTrackedObject(object obj); 
        internal abstract void StopTracking(object obj);
        internal abstract void AcceptChanges(); 
        internal abstract IEnumerable GetInterestingObjects();

        internal static ChangeTracker CreateChangeTracker(CommonDataServices dataServices, bool asReadOnly) {
            if (asReadOnly) { 
                return new ReadOnlyChangeTracker();
            } 
            else { 
                return new StandardChangeTracker(dataServices);
            } 
        }

        class StandardChangeTracker : ChangeTracker {
            Dictionary items; 
            PropertyChangingEventHandler onPropertyChanging;
            CommonDataServices services; 
 
            internal StandardChangeTracker(CommonDataServices services) {
                this.services = services; 
                this.items = new Dictionary();
                this.onPropertyChanging = new PropertyChangingEventHandler(this.OnPropertyChanging);
            }
 
            /// 
            /// Given a type root and a discriminator, return the type that would be instantiated. 
            ///  
            private static MetaType TypeFromDiscriminator(MetaType root, object discriminator) {
                foreach (MetaType type in root.InheritanceTypes) { 
                    if (IsSameDiscriminator(discriminator, type.InheritanceCode))
                        return type;
                }
                return root.InheritanceDefault; 
            }
 
            private static bool IsSameDiscriminator(object discriminator1, object discriminator2) { 
                if (discriminator1 == discriminator2) {
                    return true; 
                }
                if (discriminator1 == null || discriminator2 == null) {
                    return false;
                } 
                return discriminator1.Equals(discriminator2);
            } 
 
            internal override TrackedObject Track(object obj) {
                return Track(obj, false); 
            }

            internal override TrackedObject Track(object obj, bool recurse) {
                MetaType type = this.services.Model.GetMetaType(obj.GetType()); 
                Dictionary visited = new Dictionary();
                return Track(type, obj, visited, recurse, 1); 
            } 

            private TrackedObject Track(MetaType mt, object obj, Dictionary visited, bool recurse, int level) { 
                StandardTrackedObject tracked = (StandardTrackedObject)this.GetTrackedObject(obj);
                if (tracked != null || visited.ContainsKey(obj)) {
                    return tracked;
                } 

                // The root object tracked is tracked normally - all other objects 
                // in the reference graph are weakly tracked. 
                bool weaklyTrack = level > 1;
                tracked = new StandardTrackedObject(this, mt, obj, obj, weaklyTrack); 
                if (tracked.HasDeferredLoaders) {
                    throw Error.CannotAttachAddNonNewEntities();
                }
                this.items.Add(obj, tracked); 
                this.Attach(obj);
                visited.Add(obj, obj); 
 
                if (recurse) {
                    // track parents (objects we are dependent on) 
                    foreach (RelatedItem parent in this.services.GetParents(mt, obj)) {
                        this.Track(parent.Type, parent.Item, visited, recurse, level + 1);
                    }
 
                    // track children (objects that are dependent on us)
                    foreach (RelatedItem child in this.services.GetChildren(mt, obj)) { 
                        this.Track(child.Type, child.Item, visited, recurse, level + 1); 
                    }
                } 

                return tracked;
            }
 
            internal override void FastTrack(object obj) {
                // assumes object is already in identity cache 
                this.Attach(obj); 
            }
 
            internal override void StopTracking(object obj) {
                this.Detach(obj);
                this.items.Remove(obj);
            } 

            internal override bool IsTracked(object obj) { 
                return this.items.ContainsKey(obj) || this.IsFastTracked(obj); 
            }
 
            private bool IsFastTracked(object obj) {
                MetaType type = this.services.Model.GetTable(obj.GetType()).RowType;
                return this.services.IsCachedObject(type, obj);
            } 

            internal override TrackedObject GetTrackedObject(object obj) { 
                StandardTrackedObject ti; 
                if (!this.items.TryGetValue(obj, out ti)) {
                    if (this.IsFastTracked(obj)) { 
                        return this.PromoteFastTrackedObject(obj);
                    }
                }
                return ti; 
            }
 
            private StandardTrackedObject PromoteFastTrackedObject(object obj) { 
                Type type = obj.GetType();
                MetaType metaType = this.services.Model.GetTable(type).RowType.GetInheritanceType(type); 
                return this.PromoteFastTrackedObject(metaType, obj);
            }

            private StandardTrackedObject PromoteFastTrackedObject(MetaType type, object obj) { 
                StandardTrackedObject ti = new StandardTrackedObject(this, type, obj, obj);
                this.items.Add(obj, ti); 
                return ti; 
            }
 
            private void Attach(object obj) {
                INotifyPropertyChanging notifier = obj as INotifyPropertyChanging;
                if (notifier != null) {
                    notifier.PropertyChanging += this.onPropertyChanging; 
                }
                else { 
                    // if has no notifier, consider it modified already 
                    this.OnPropertyChanging(obj, null);
                } 
            }

            private void Detach(object obj) {
                INotifyPropertyChanging notifier = obj as INotifyPropertyChanging; 
                if (notifier != null) {
                    notifier.PropertyChanging -= this.onPropertyChanging; 
                } 
            }
 
            private void OnPropertyChanging(object sender, PropertyChangingEventArgs args) {
                StandardTrackedObject ti;
                if (this.items.TryGetValue(sender, out ti)) {
                    ti.StartTracking(); 
                }
                else if (this.IsFastTracked(sender)) { 
                    ti = this.PromoteFastTrackedObject(sender); 
                    ti.StartTracking();
                } 
            }

            internal override void AcceptChanges() {
                List list = new List((IEnumerable)this.items.Values); 
                foreach (TrackedObject item in list) {
                    item.AcceptChanges(); 
                } 
            }
 
            internal override IEnumerable GetInterestingObjects() {
                foreach (StandardTrackedObject ti in this.items.Values) {
                    if (ti.IsInteresting) {
                        yield return ti; 
                    }
                } 
            } 

            class StandardTrackedObject : TrackedObject { 
                private StandardChangeTracker tracker;
                private MetaType type;
                private object current;
                private object original; 
                private State state;
                private BitArray dirtyMemberCache; 
                private bool haveInitializedDeferredLoaders; 
                private bool isWeaklyTracked;
 
                enum State {
                    New,
                    Deleted,
                    PossiblyModified, 
                    Modified,
                    Removed, 
                    Dead 
                }
 
                public override string ToString() {
                    return type.Name + ":" + GetState();
                }
 
                private string GetState() {
                    switch (this.state) { 
                        case State.New: 
                        case State.Deleted:
                        case State.Dead: 
                        case State.Removed:
                            return this.state.ToString();
                        default:
                            if (this.IsModified) { 
                                return "Modified";
                            } 
                            else { 
                                return "Unmodified";
                            } 
                    }
                }

                internal StandardTrackedObject(StandardChangeTracker tracker, MetaType type, object current, object original) { 
                    if (current == null) {
                        throw Error.ArgumentNull("current"); 
                    } 
                    this.tracker = tracker;
                    this.type = type.GetInheritanceType(current.GetType()); 
                    this.current = current;
                    this.original = original;
                    this.state = State.PossiblyModified;
                    dirtyMemberCache = new BitArray(this.type.DataMembers.Count); 
                }
 
                internal StandardTrackedObject(StandardChangeTracker tracker, MetaType type, object current, object original, bool isWeaklyTracked) 
                    : this(tracker, type, current, original) {
                    this.isWeaklyTracked = isWeaklyTracked; 
                }

                internal override bool IsWeaklyTracked {
                    get { return isWeaklyTracked; } 
                }
 
                internal override MetaType Type { 
                    get { return this.type; }
                } 

                internal override object Current {
                    get { return this.current; }
                } 

                internal override object Original { 
                    get { return this.original; } 
                }
 
                internal override bool IsNew {
                    get { return this.state == State.New; }
                }
 
                internal override bool IsDeleted {
                    get { return this.state == State.Deleted; } 
                } 

                internal override bool IsRemoved { 
                    get { return this.state == State.Removed; }
                }

                internal override bool IsDead { 
                    get { return this.state == State.Dead; }
                } 
 
                internal override bool IsModified {
                    get { return this.state == State.Modified || (this.state == State.PossiblyModified && this.current != this.original && this.HasChangedValues()); } 
                }

                internal override bool IsUnmodified {
                    get { return this.state == State.PossiblyModified && (this.current == this.original || !this.HasChangedValues()); } 
                }
 
                internal override bool IsPossiblyModified { 
                    get { return this.state == State.Modified || this.state == State.PossiblyModified; }
                } 

                internal override bool CanInferDelete() {
                    // A delete can be inferred iff there is a non-nullable singleton association that has
                    // been set to null, and the association has DeleteOnNull = true. 
                    if (this.state == State.Modified || this.state == State.PossiblyModified) {
                        foreach (MetaAssociation assoc in Type.Associations) { 
                            if (assoc.DeleteOnNull && assoc.IsForeignKey && !assoc.IsNullable && !assoc.IsMany && 
                                assoc.ThisMember.StorageAccessor.HasAssignedValue(Current) &&
                                assoc.ThisMember.StorageAccessor.GetBoxedValue(Current) == null) { 
                                return true;
                            }
                        }
                    } 
                    return false;
                } 
 
                internal override bool IsInteresting {
                    get { 
                        return this.state == State.New ||
                               this.state == State.Deleted ||
                               this.state == State.Modified ||
                               (this.state == State.PossiblyModified && this.current != this.original) || 
                               CanInferDelete();
                    } 
                } 

                internal override void ConvertToNew() { 
                    // must be new or unmodified or removed to convert to new
                    System.Diagnostics.Debug.Assert(this.IsNew || this.IsRemoved || this.IsUnmodified);
                    this.original = null;
                    this.state = State.New; 
                }
 
                internal override void ConvertToPossiblyModified() { 
                    System.Diagnostics.Debug.Assert(this.IsPossiblyModified || this.IsDeleted);
                    this.state = State.PossiblyModified; 
                    this.isWeaklyTracked = false;
                }

                internal override void ConvertToModified() { 
                    System.Diagnostics.Debug.Assert(this.IsPossiblyModified);
                    System.Diagnostics.Debug.Assert(this.type.VersionMember != null || !this.type.HasUpdateCheck); 
                    this.state = State.Modified; 
                    this.isWeaklyTracked = false;
                } 

                internal override void ConvertToPossiblyModified(object originalState) {
                    // must be modified or unmodified to convert to modified
                    System.Diagnostics.Debug.Assert(this.IsNew || this.IsPossiblyModified); 
                    System.Diagnostics.Debug.Assert(originalState != null);
                    System.Diagnostics.Debug.Assert(originalState.GetType() == this.type.Type); 
                    this.state = State.PossiblyModified; 
                    this.original = this.CreateDataCopy(originalState);
                    this.isWeaklyTracked = false; 
                }

                internal override void ConvertToDeleted() {
                    // must be modified or unmodified to be deleted 
                    System.Diagnostics.Debug.Assert(this.IsDeleted || this.IsPossiblyModified);
                    this.state = State.Deleted; 
                    this.isWeaklyTracked = false; 
                }
 
                internal override void ConvertToDead() {
                    System.Diagnostics.Debug.Assert(this.IsDead || this.IsDeleted);
                    this.state = State.Dead;
                    this.isWeaklyTracked = false; 
                }
 
                internal override void ConvertToRemoved() { 
                    System.Diagnostics.Debug.Assert(this.IsRemoved || this.IsNew);
                    this.state = State.Removed; 
                    this.isWeaklyTracked = false;
                }

                internal override void ConvertToUnmodified() { 
                    System.Diagnostics.Debug.Assert(this.IsNew || this.IsPossiblyModified);
                    // reset to unmodified 
                    this.state = State.PossiblyModified; 
                    if (this.current is INotifyPropertyChanging) {
                        this.original = this.current; 
                    }
                    else {
                        this.original = this.CreateDataCopy(this.current);
                    } 
                    this.ResetDirtyMemberTracking();
                    this.isWeaklyTracked = false; 
                } 

                internal override void AcceptChanges() { 
                    if (IsWeaklyTracked) {
                        InitializeDeferredLoaders();
                        isWeaklyTracked = false;
                    } 
                    if (this.IsDeleted) {
                        this.ConvertToDead(); 
                    } 
                    else if (this.IsNew) {
                        this.InitializeDeferredLoaders(); 
                        this.ConvertToUnmodified();
                    }
                    else if (this.IsPossiblyModified) {
                        this.ConvertToUnmodified(); 
                    }
                } 
 
                private void AssignMember(object instance, MetaDataMember mm, object value) {
                    // In the unnotified case, directly use the storage accessor 
                    // for everything because there are not events to be fired.
                    if (!(this.current is INotifyPropertyChanging)) {
                        mm.StorageAccessor.SetBoxedValue(ref instance, value);
                    } 
                    else {
                        // Go through the member accessor to fire events. 
                        mm.MemberAccessor.SetBoxedValue(ref instance, value); 
                    }
                } 

                /// 
                /// Certain state is saved during change tracking to enable modifications
                /// to be detected taking refresh operations into account.  When changes 
                /// are reverted or accepted, this state must be reset.
                ///  
                private void ResetDirtyMemberTracking() { 
                    this.dirtyMemberCache.SetAll(false);
                } 

                /// 
                /// Refresh internal tracking state using the original value and mode
                /// specified. 
                /// 
                internal override void Refresh(RefreshMode mode, object freshInstance) { 
                    this.SynchDependentData(); 

                    // This must be done prior to updating original values 
                    this.UpdateDirtyMemberCache();

                    // Apply the refresh strategy to each data member
                    Type instanceType = freshInstance.GetType(); 
                    foreach (MetaDataMember mm in type.PersistentDataMembers) {
                        var memberMode = mm.IsDbGenerated ? RefreshMode.OverwriteCurrentValues : mode; 
                        if (memberMode != RefreshMode.KeepCurrentValues) { 
                            if (!mm.IsAssociation && (this.Type.Type == instanceType || mm.DeclaringType.Type.IsAssignableFrom(instanceType))) {
                                object freshValue = mm.StorageAccessor.GetBoxedValue(freshInstance); 
                                this.RefreshMember(mm, memberMode, freshValue);
                            }
                        }
                    } 

                    // Make the new data the current original value 
                    this.original = this.CreateDataCopy(freshInstance); 

                    if (mode == RefreshMode.OverwriteCurrentValues) { 
                        this.ResetDirtyMemberTracking();
                    }
                }
 
                /// 
                /// Using the last saved comparison baseline, figure out which members have 
                /// changed since the last refresh, and save that information.  This must be 
                /// done BEFORE any merge operations modify the current values.
                ///  
                private void UpdateDirtyMemberCache() {
                    // iterate over all members, and if they differ from
                    // last read values, mark as dirty
                    foreach (MetaDataMember mm in type.PersistentDataMembers) { 
                        if (mm.IsAssociation && mm.Association.IsMany) {
                            continue; 
                        } 
                        if (!this.dirtyMemberCache.Get(mm.Ordinal) && this.HasChangedValue(mm)) {
                            this.dirtyMemberCache.Set(mm.Ordinal, true); 
                        }
                    }
                }
 
                internal override void RefreshMember(MetaDataMember mm, RefreshMode mode, object freshValue) {
                    System.Diagnostics.Debug.Assert(!mm.IsAssociation); 
 
                    if (mode == RefreshMode.KeepCurrentValues) {
                        return; 
                    }

                    bool hasUserChange = this.HasChangedValue(mm);
 
                    // we don't want to overwrite any modified values, unless
                    // the mode is original wins 
                    if (hasUserChange && mode != RefreshMode.OverwriteCurrentValues) 
                        return;
 
                    object currentValue = mm.StorageAccessor.GetBoxedValue(this.current);
                    if (!object.Equals(freshValue, currentValue)) {
                        mm.StorageAccessor.SetBoxedValue(ref this.current, freshValue);
 
                        // update all singleton associations that are affected by a change to this member
                        foreach (MetaDataMember am in this.GetAssociationsForKey(mm)) { 
                            if (!am.Association.IsMany) { 
                                IEnumerable ds = this.tracker.services.GetDeferredSourceFactory(am).CreateDeferredSource(this.current);
                                if (am.StorageAccessor.HasValue(this.current)) { 
                                    this.AssignMember(this.current, am, ds.Cast().SingleOrDefault());
                                }
                            }
                        } 
                    }
                } 
 
                private IEnumerable GetAssociationsForKey(MetaDataMember key) {
                    foreach (MetaDataMember mm in this.type.PersistentDataMembers) { 
                        if (mm.IsAssociation && mm.Association.ThisKey.Contains(key)) {
                            yield return mm;
                        }
                    } 
                }
 
                internal override object CreateDataCopy(object instance) { 
                    System.Diagnostics.Debug.Assert(instance != null);
                    Type instanceType = instance.GetType(); 
                    System.Diagnostics.Debug.Assert(instance.GetType() == this.type.Type);

                    object copy = Activator.CreateInstance(this.Type.Type);
 
                    MetaType rootMetaType = this.tracker.services.Model.GetTable(instanceType).RowType.InheritanceRoot;
                    foreach (MetaDataMember mm in rootMetaType.GetInheritanceType(instanceType).PersistentDataMembers) { 
                        if (this.Type.Type != instanceType && !mm.DeclaringType.Type.IsAssignableFrom(instanceType)) { 
                            continue;
                        } 
                        if (mm.IsDeferred) {
                            // do not copy associations
                            if (!mm.IsAssociation) {
                                if (mm.StorageAccessor.HasValue(instance)) { 
                                    object value = mm.DeferredValueAccessor.GetBoxedValue(instance);
                                    mm.DeferredValueAccessor.SetBoxedValue(ref copy, value); 
                                } 
                                else {
                                    IEnumerable ds = this.tracker.services.GetDeferredSourceFactory(mm).CreateDeferredSource(copy); 
                                    mm.DeferredSourceAccessor.SetBoxedValue(ref copy, ds);
                                }
                            }
                        } 
                        else {
                            // otherwise assign the value as-is to the backup instance 
                            object value = mm.StorageAccessor.GetBoxedValue(instance); 
                            // assumes member values are immutable or will communicate changes to entity
                            // note: byte[] and char[] don't do this. 
                            mm.StorageAccessor.SetBoxedValue(ref copy, value);
                        }
                    }
                    return copy; 
                }
 
                internal void StartTracking() { 
                    if (this.original == this.current) {
                        this.original = this.CreateDataCopy(this.current); 
                    }
                }

                // Return value indicates whether or not any data was actually [....]'d 
                internal override bool SynchDependentData() {
                    bool valueWasSet = false; 
 
                    // set foreign key fields
                    foreach (MetaAssociation assoc in this.Type.Associations) { 
                        MetaDataMember mm = assoc.ThisMember;
                        if (assoc.IsForeignKey) {
                            bool hasAssigned = mm.StorageAccessor.HasAssignedValue(this.current);
                            bool hasLoaded = mm.StorageAccessor.HasLoadedValue(this.current); 
                            if (hasAssigned || hasLoaded) {
                                object parent = mm.StorageAccessor.GetBoxedValue(this.current); 
                                if (parent != null) { 
                                    // copy parent's current primary key into this instance's foreign key fields
                                    for (int i = 0, n = assoc.ThisKey.Count; i < n; i++) { 
                                        MetaDataMember accThis = assoc.ThisKey[i];
                                        MetaDataMember accParent = assoc.OtherKey[i];
                                        object parentValue = accParent.StorageAccessor.GetBoxedValue(parent);
                                        accThis.StorageAccessor.SetBoxedValue(ref this.current, parentValue); 
                                        valueWasSet = true;
                                    } 
                                } 
                                else if (assoc.IsNullable) {
                                    if (mm.IsDeferred || (this.original != null && mm.MemberAccessor.GetBoxedValue(this.original) != null)) { 
                                        // no known parent? set to null
                                        for (int i = 0, n = assoc.ThisKey.Count; i < n; i++) {
                                            MetaDataMember accThis = assoc.ThisKey[i];
                                            if (accThis.CanBeNull) { 
                                                if (this.original != null && this.HasChangedValue(accThis)) {
                                                    if (accThis.StorageAccessor.GetBoxedValue(this.current) != null) { 
                                                        throw Error.InconsistentAssociationAndKeyChange(accThis.Member.Name, mm.Member.Name); 
                                                    }
                                                } 
                                                else {
                                                    accThis.StorageAccessor.SetBoxedValue(ref this.current, null);
                                                    valueWasSet = true;
                                                } 
                                            }
                                        } 
                                    } 
                                }
                                else if (!hasLoaded) { 
                                    //Else the parent association has been set to null; but the ID is not nullable so
                                    //the value can not be set
                                    StringBuilder keys = new StringBuilder();
                                    foreach (MetaDataMember key in assoc.ThisKey) { 
                                        if (keys.Length > 0) {
                                            keys.Append(", "); 
                                        } 
                                        keys.AppendFormat("{0}.{1}", this.Type.Name.ToString(), key.Name);
                                    } 
                                    throw Error.CouldNotRemoveRelationshipBecauseOneSideCannotBeNull(assoc.OtherType.Name, this.Type.Name, keys);
                                }
                            }
                        } 
                    }
 
                    /// Explicitly set any inheritance discriminator for item. 
                    if (this.type.HasInheritance) {
                        if (this.original != null) { 
                            object currentDiscriminator = type.Discriminator.MemberAccessor.GetBoxedValue(this.current);
                            MetaType currentTypeFromDiscriminator = TypeFromDiscriminator(this.type, currentDiscriminator);
                            object dbDiscriminator = type.Discriminator.MemberAccessor.GetBoxedValue(this.original);
                            MetaType dbTypeFromDiscriminator = TypeFromDiscriminator(this.type, dbDiscriminator); 

                            // Would the discriminator change also change the type? If so, its not allowed. 
                            if (currentTypeFromDiscriminator != dbTypeFromDiscriminator) { 
                                throw Error.CannotChangeInheritanceType(dbDiscriminator,
                                    currentDiscriminator, original.GetType().Name, currentTypeFromDiscriminator); 
                            }
                        }
                        else {
                            // No db value means this is an 'Add'. Set the discriminator. 
                            MetaType currentType = type.GetInheritanceType(this.current.GetType());
                            if (currentType.HasInheritanceCode) { 
                                object code = currentType.InheritanceCode; 
                                this.type.Discriminator.MemberAccessor.SetBoxedValue(ref current, code);
                                valueWasSet = true; 
                            }
                        }
                    }
                    return valueWasSet; 
                }
 
                internal override bool HasChangedValue(MetaDataMember mm) { 
                    if (this.current == this.original) {
                        return false; 
                    }
                    if (mm.IsAssociation && mm.Association.IsMany) {
                        return mm.StorageAccessor.HasAssignedValue(this.original);
                    } 
                    if (mm.StorageAccessor.HasValue(this.current)) {
                        if (this.original != null && mm.StorageAccessor.HasValue(this.original)) { 
                            // If the member has ever been in a modified state 
                            // in the past, it is considered modified
                            if (dirtyMemberCache.Get(mm.Ordinal)) { 
                                return true;
                            }
                            object baseline = mm.MemberAccessor.GetBoxedValue(this.original);
                            object currentValue = mm.MemberAccessor.GetBoxedValue(this.current); 
                            if (!object.Equals(currentValue, baseline)) {
                                return true; 
                            } 
                            return false;
                        } 
                        else if (mm.IsDeferred && mm.StorageAccessor.HasAssignedValue(this.current)) {
                            return true;
                        }
                    } 
                    return false;
                } 
 
                internal override bool HasChangedValues() {
                    if (this.current == this.original) { 
                        return false;
                    }
                    if (this.IsNew) {
                        return true; 
                    }
                    foreach (MetaDataMember mm in this.type.PersistentDataMembers) { 
                        if (!mm.IsAssociation && this.HasChangedValue(mm)) { 
                            return true;
                        } 
                    }
                    return false;
                }
 
                internal override IEnumerable GetModifiedMembers() {
                    foreach (MetaDataMember mm in this.type.PersistentDataMembers) { 
                        if (this.IsModifiedMember(mm)) { 
                            object currentValue = mm.MemberAccessor.GetBoxedValue(this.current);
                            if (this.original != null && mm.StorageAccessor.HasValue(this.original)) { 
                                object originalValue = mm.MemberAccessor.GetBoxedValue(this.original);
                                yield return new ModifiedMemberInfo(mm.Member, currentValue, originalValue);
                            }
                            else if (this.original == null || (mm.IsDeferred && !mm.StorageAccessor.HasLoadedValue(this.current))) { 
                                yield return new ModifiedMemberInfo(mm.Member, currentValue, null);
                            } 
                        } 
                    }
                } 

                private bool IsModifiedMember(MetaDataMember member) {
                    return !member.IsAssociation &&
                           !member.IsPrimaryKey && 
                           !member.IsVersion &&
                           !member.IsDbGenerated && 
                            member.StorageAccessor.HasAssignedValue(this.current) && 
                           (this.state == State.Modified ||
                           (this.state == State.PossiblyModified && this.HasChangedValue(member))); 
                }

                internal override bool HasDeferredLoaders {
                    get { 
                        foreach (MetaAssociation assoc in this.Type.Associations) {
                            if (HasDeferredLoader(assoc.ThisMember)) { 
                                return true; 
                            }
                        } 
                        IEnumerable deferredMembers = this.Type.PersistentDataMembers.Where(p => p.IsDeferred && !p.IsAssociation);
                        foreach (MetaDataMember deferredMember in deferredMembers) {
                            if (HasDeferredLoader(deferredMember)) {
                                return true; 
                            }
                        } 
                        return false; 
                    }
                } 

                private bool HasDeferredLoader(MetaDataMember deferredMember) {
                    if (!deferredMember.IsDeferred) {
                        return false; 
                    }
 
                    MetaAccessor acc = deferredMember.StorageAccessor; 
                    if (acc.HasAssignedValue(this.current) || acc.HasLoadedValue(this.current)) {
                        return false; 
                    }
                    MetaAccessor dsacc = deferredMember.DeferredSourceAccessor;
                    IEnumerable loader = (IEnumerable)dsacc.GetBoxedValue(this.current);
 
                    return loader != null;
                } 
 
                /// 
                /// Called to initialize deferred loaders for New or Attached entities. 
                /// 
                internal override void InitializeDeferredLoaders() {
                    if (this.tracker.services.Context.DeferredLoadingEnabled) {
                        foreach (MetaAssociation assoc in this.Type.Associations) { 
                            // don't set loader on association that is dependent on unrealized generated values
                            if (!this.IsPendingGeneration(assoc.ThisKey)) { 
                                InitializeDeferredLoader(assoc.ThisMember); 
                            }
                        } 
                        IEnumerable deferredMembers = this.Type.PersistentDataMembers.Where(p => p.IsDeferred && !p.IsAssociation);
                        foreach (MetaDataMember deferredMember in deferredMembers) {
                            // don't set loader on member that is dependent on unrealized generated values
                            if (!this.IsPendingGeneration(Type.IdentityMembers)) { 
                                InitializeDeferredLoader(deferredMember);
                            } 
                        } 
                        haveInitializedDeferredLoaders = true;
                    } 
                }

                private void InitializeDeferredLoader(MetaDataMember deferredMember) {
                    MetaAccessor acc = deferredMember.StorageAccessor; 
                    if (!acc.HasAssignedValue(this.current) && !acc.HasLoadedValue(this.current)) {
                        MetaAccessor dsacc = deferredMember.DeferredSourceAccessor; 
                        IEnumerable loader = (IEnumerable)dsacc.GetBoxedValue(this.current); 
                        // don't reset loader on any deferred member that already has one
                        if (loader == null) { 
                            IDeferredSourceFactory factory = this.tracker.services.GetDeferredSourceFactory(deferredMember);
                            loader = factory.CreateDeferredSource(this.current);
                            dsacc.SetBoxedValue(ref this.current, loader);
 
                        }
                        else if (loader != null && !haveInitializedDeferredLoaders) { 
                            // If loader is present but wasn't generated by us, then 
                            // an attempt to Attach or Add an entity from another context
                            // has been made, which is not supported. 
                            throw Error.CannotAttachAddNonNewEntities();
                        }
                    }
                } 

                internal override bool IsPendingGeneration(IEnumerable key) { 
                    if (this.IsNew) { 
                        foreach (MetaDataMember member in key) {
                            if (IsMemberPendingGeneration(member)) { 
                                return true;
                            }
                        }
                    } 
                    return false;
                } 
 
                internal override bool IsMemberPendingGeneration(MetaDataMember keyMember) {
                    if (this.IsNew && keyMember.IsDbGenerated) { 
                        return true;
                    }
                    // look for any FK association that has this key member (should only be one)
                    foreach (MetaAssociation assoc in type.Associations) { 
                        if (assoc.IsForeignKey) {
                            int index = assoc.ThisKey.IndexOf(keyMember); 
                            if (index > -1) { 
                                // we must have a reference to this other object to know if its side of
                                // the association is generated or not 
                                object otherItem = null;
                                if (assoc.ThisMember.IsDeferred) {
                                    otherItem = assoc.ThisMember.DeferredValueAccessor.GetBoxedValue(this.current);
                                } 
                                else {
                                    otherItem = assoc.ThisMember.StorageAccessor.GetBoxedValue(this.current); 
                                } 
                                if (otherItem != null) {
                                    if (assoc.IsMany) { 
                                        // Can't be pending generation for a value that would have to be the same
                                        // across many rows.
                                        continue;
                                    } 
                                    else {
                                        StandardTrackedObject trackedOther = (StandardTrackedObject)this.tracker.GetTrackedObject(otherItem); 
                                        if (trackedOther != null) { 
                                            MetaDataMember otherMember = assoc.OtherKey[index];
                                            return trackedOther.IsMemberPendingGeneration(otherMember); 
                                        }
                                    }
                                }
                            } 
                        }
                    } 
                    return false; 
                }
            } 
        }

        /// 
        /// This is the implementation used when change tracking is disabled. 
        /// 
        class ReadOnlyChangeTracker : ChangeTracker { 
            internal override TrackedObject Track(object obj) { return null; } 
            internal override TrackedObject Track(object obj, bool recurse) { return null; }
            internal override void FastTrack(object obj) { } 
            internal override bool IsTracked(object obj) { return false; }
            internal override TrackedObject GetTrackedObject(object obj) { return null; }
            internal override void StopTracking(object obj) { }
            internal override void AcceptChanges() { } 
            internal override IEnumerable GetInterestingObjects() { return new TrackedObject[0]; }
        } 
    } 

    internal abstract class TrackedObject { 
        internal abstract MetaType Type { get; }
        /// 
        /// The current client value.
        ///  
        internal abstract object Current { get; }
        ///  
        /// The last read database value.  This is updated whenever the 
        /// item is refreshed.
        ///  
        internal abstract object Original { get; }
        internal abstract bool IsInteresting { get; } // new, deleted or possibly changed
        internal abstract bool IsNew { get; }
        internal abstract bool IsDeleted { get; } 
        internal abstract bool IsModified { get; }
        internal abstract bool IsUnmodified { get; } 
        internal abstract bool IsPossiblyModified { get; } 
        internal abstract bool IsRemoved { get; }
        internal abstract bool IsDead { get; } 
        /// 
        /// True if the object is being tracked (perhaps during a recursive
        /// attach operation) but can be transitioned to other states.
        ///  
        internal abstract bool IsWeaklyTracked { get; }
        internal abstract bool HasDeferredLoaders { get; } 
        internal abstract bool HasChangedValues(); 
        internal abstract IEnumerable GetModifiedMembers();
        internal abstract bool HasChangedValue(MetaDataMember mm); 
        internal abstract bool CanInferDelete();
        internal abstract void AcceptChanges();
        internal abstract void ConvertToNew();
        internal abstract void ConvertToPossiblyModified(); 
        internal abstract void ConvertToPossiblyModified(object original);
        internal abstract void ConvertToUnmodified(); 
        internal abstract void ConvertToModified(); 
        internal abstract void ConvertToDeleted();
        internal abstract void ConvertToRemoved(); 
        internal abstract void ConvertToDead();
        /// 
        /// Refresh the item by making the value passed in the current
        /// Database value, and refreshing the current values using the 
        /// mode specified.
        ///  
        internal abstract void Refresh(RefreshMode mode, object freshInstance); 
        /// 
        /// Does the refresh operation for a single member.  This method does not 
        /// update the baseline 'original' value.  You must call
        /// Refresh(RefreshMode.KeepCurrentValues, freshInstance) to finish the refresh
        /// after refreshing individual members.
        ///  
        /// 
        ///  
        ///  
        internal abstract void RefreshMember(MetaDataMember member, RefreshMode mode, object freshValue);
        ///  
        /// Create a data-member only copy of the instance (no associations)
        /// 
        /// 
        internal abstract object CreateDataCopy(object instance); 

        internal abstract bool SynchDependentData(); 
 
        internal abstract bool IsPendingGeneration(IEnumerable keyMembers);
        internal abstract bool IsMemberPendingGeneration(MetaDataMember keyMember); 

        internal abstract void InitializeDeferredLoaders();
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
using System; 
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; 
using System.Reflection;
using System.Text; 
 
namespace System.Data.Linq {
    using System.Data.Linq.Mapping; 
    using System.Data.Linq.Provider;

    internal abstract class ChangeTracker {
        ///  
        /// Starts tracking an object as 'unchanged'
        ///  
        ///  
        /// 
        internal abstract TrackedObject Track(object obj); 
        /// 
        /// Starts tracking an object as 'unchanged', and optionally
        /// 'weakly' tracks all other referenced objects recursively.
        ///  
        /// 
        /// True if all untracked objects in the graph 
        /// should be tracked recursively. 
        /// 
        internal abstract TrackedObject Track(object obj, bool recurse); 
        /// 
        /// Fast-tracks an object that is already in identity cache
        /// 
        ///  
        internal abstract void FastTrack(object obj);
        internal abstract bool IsTracked(object obj); 
        internal abstract TrackedObject GetTrackedObject(object obj); 
        internal abstract void StopTracking(object obj);
        internal abstract void AcceptChanges(); 
        internal abstract IEnumerable GetInterestingObjects();

        internal static ChangeTracker CreateChangeTracker(CommonDataServices dataServices, bool asReadOnly) {
            if (asReadOnly) { 
                return new ReadOnlyChangeTracker();
            } 
            else { 
                return new StandardChangeTracker(dataServices);
            } 
        }

        class StandardChangeTracker : ChangeTracker {
            Dictionary items; 
            PropertyChangingEventHandler onPropertyChanging;
            CommonDataServices services; 
 
            internal StandardChangeTracker(CommonDataServices services) {
                this.services = services; 
                this.items = new Dictionary();
                this.onPropertyChanging = new PropertyChangingEventHandler(this.OnPropertyChanging);
            }
 
            /// 
            /// Given a type root and a discriminator, return the type that would be instantiated. 
            ///  
            private static MetaType TypeFromDiscriminator(MetaType root, object discriminator) {
                foreach (MetaType type in root.InheritanceTypes) { 
                    if (IsSameDiscriminator(discriminator, type.InheritanceCode))
                        return type;
                }
                return root.InheritanceDefault; 
            }
 
            private static bool IsSameDiscriminator(object discriminator1, object discriminator2) { 
                if (discriminator1 == discriminator2) {
                    return true; 
                }
                if (discriminator1 == null || discriminator2 == null) {
                    return false;
                } 
                return discriminator1.Equals(discriminator2);
            } 
 
            internal override TrackedObject Track(object obj) {
                return Track(obj, false); 
            }

            internal override TrackedObject Track(object obj, bool recurse) {
                MetaType type = this.services.Model.GetMetaType(obj.GetType()); 
                Dictionary visited = new Dictionary();
                return Track(type, obj, visited, recurse, 1); 
            } 

            private TrackedObject Track(MetaType mt, object obj, Dictionary visited, bool recurse, int level) { 
                StandardTrackedObject tracked = (StandardTrackedObject)this.GetTrackedObject(obj);
                if (tracked != null || visited.ContainsKey(obj)) {
                    return tracked;
                } 

                // The root object tracked is tracked normally - all other objects 
                // in the reference graph are weakly tracked. 
                bool weaklyTrack = level > 1;
                tracked = new StandardTrackedObject(this, mt, obj, obj, weaklyTrack); 
                if (tracked.HasDeferredLoaders) {
                    throw Error.CannotAttachAddNonNewEntities();
                }
                this.items.Add(obj, tracked); 
                this.Attach(obj);
                visited.Add(obj, obj); 
 
                if (recurse) {
                    // track parents (objects we are dependent on) 
                    foreach (RelatedItem parent in this.services.GetParents(mt, obj)) {
                        this.Track(parent.Type, parent.Item, visited, recurse, level + 1);
                    }
 
                    // track children (objects that are dependent on us)
                    foreach (RelatedItem child in this.services.GetChildren(mt, obj)) { 
                        this.Track(child.Type, child.Item, visited, recurse, level + 1); 
                    }
                } 

                return tracked;
            }
 
            internal override void FastTrack(object obj) {
                // assumes object is already in identity cache 
                this.Attach(obj); 
            }
 
            internal override void StopTracking(object obj) {
                this.Detach(obj);
                this.items.Remove(obj);
            } 

            internal override bool IsTracked(object obj) { 
                return this.items.ContainsKey(obj) || this.IsFastTracked(obj); 
            }
 
            private bool IsFastTracked(object obj) {
                MetaType type = this.services.Model.GetTable(obj.GetType()).RowType;
                return this.services.IsCachedObject(type, obj);
            } 

            internal override TrackedObject GetTrackedObject(object obj) { 
                StandardTrackedObject ti; 
                if (!this.items.TryGetValue(obj, out ti)) {
                    if (this.IsFastTracked(obj)) { 
                        return this.PromoteFastTrackedObject(obj);
                    }
                }
                return ti; 
            }
 
            private StandardTrackedObject PromoteFastTrackedObject(object obj) { 
                Type type = obj.GetType();
                MetaType metaType = this.services.Model.GetTable(type).RowType.GetInheritanceType(type); 
                return this.PromoteFastTrackedObject(metaType, obj);
            }

            private StandardTrackedObject PromoteFastTrackedObject(MetaType type, object obj) { 
                StandardTrackedObject ti = new StandardTrackedObject(this, type, obj, obj);
                this.items.Add(obj, ti); 
                return ti; 
            }
 
            private void Attach(object obj) {
                INotifyPropertyChanging notifier = obj as INotifyPropertyChanging;
                if (notifier != null) {
                    notifier.PropertyChanging += this.onPropertyChanging; 
                }
                else { 
                    // if has no notifier, consider it modified already 
                    this.OnPropertyChanging(obj, null);
                } 
            }

            private void Detach(object obj) {
                INotifyPropertyChanging notifier = obj as INotifyPropertyChanging; 
                if (notifier != null) {
                    notifier.PropertyChanging -= this.onPropertyChanging; 
                } 
            }
 
            private void OnPropertyChanging(object sender, PropertyChangingEventArgs args) {
                StandardTrackedObject ti;
                if (this.items.TryGetValue(sender, out ti)) {
                    ti.StartTracking(); 
                }
                else if (this.IsFastTracked(sender)) { 
                    ti = this.PromoteFastTrackedObject(sender); 
                    ti.StartTracking();
                } 
            }

            internal override void AcceptChanges() {
                List list = new List((IEnumerable)this.items.Values); 
                foreach (TrackedObject item in list) {
                    item.AcceptChanges(); 
                } 
            }
 
            internal override IEnumerable GetInterestingObjects() {
                foreach (StandardTrackedObject ti in this.items.Values) {
                    if (ti.IsInteresting) {
                        yield return ti; 
                    }
                } 
            } 

            class StandardTrackedObject : TrackedObject { 
                private StandardChangeTracker tracker;
                private MetaType type;
                private object current;
                private object original; 
                private State state;
                private BitArray dirtyMemberCache; 
                private bool haveInitializedDeferredLoaders; 
                private bool isWeaklyTracked;
 
                enum State {
                    New,
                    Deleted,
                    PossiblyModified, 
                    Modified,
                    Removed, 
                    Dead 
                }
 
                public override string ToString() {
                    return type.Name + ":" + GetState();
                }
 
                private string GetState() {
                    switch (this.state) { 
                        case State.New: 
                        case State.Deleted:
                        case State.Dead: 
                        case State.Removed:
                            return this.state.ToString();
                        default:
                            if (this.IsModified) { 
                                return "Modified";
                            } 
                            else { 
                                return "Unmodified";
                            } 
                    }
                }

                internal StandardTrackedObject(StandardChangeTracker tracker, MetaType type, object current, object original) { 
                    if (current == null) {
                        throw Error.ArgumentNull("current"); 
                    } 
                    this.tracker = tracker;
                    this.type = type.GetInheritanceType(current.GetType()); 
                    this.current = current;
                    this.original = original;
                    this.state = State.PossiblyModified;
                    dirtyMemberCache = new BitArray(this.type.DataMembers.Count); 
                }
 
                internal StandardTrackedObject(StandardChangeTracker tracker, MetaType type, object current, object original, bool isWeaklyTracked) 
                    : this(tracker, type, current, original) {
                    this.isWeaklyTracked = isWeaklyTracked; 
                }

                internal override bool IsWeaklyTracked {
                    get { return isWeaklyTracked; } 
                }
 
                internal override MetaType Type { 
                    get { return this.type; }
                } 

                internal override object Current {
                    get { return this.current; }
                } 

                internal override object Original { 
                    get { return this.original; } 
                }
 
                internal override bool IsNew {
                    get { return this.state == State.New; }
                }
 
                internal override bool IsDeleted {
                    get { return this.state == State.Deleted; } 
                } 

                internal override bool IsRemoved { 
                    get { return this.state == State.Removed; }
                }

                internal override bool IsDead { 
                    get { return this.state == State.Dead; }
                } 
 
                internal override bool IsModified {
                    get { return this.state == State.Modified || (this.state == State.PossiblyModified && this.current != this.original && this.HasChangedValues()); } 
                }

                internal override bool IsUnmodified {
                    get { return this.state == State.PossiblyModified && (this.current == this.original || !this.HasChangedValues()); } 
                }
 
                internal override bool IsPossiblyModified { 
                    get { return this.state == State.Modified || this.state == State.PossiblyModified; }
                } 

                internal override bool CanInferDelete() {
                    // A delete can be inferred iff there is a non-nullable singleton association that has
                    // been set to null, and the association has DeleteOnNull = true. 
                    if (this.state == State.Modified || this.state == State.PossiblyModified) {
                        foreach (MetaAssociation assoc in Type.Associations) { 
                            if (assoc.DeleteOnNull && assoc.IsForeignKey && !assoc.IsNullable && !assoc.IsMany && 
                                assoc.ThisMember.StorageAccessor.HasAssignedValue(Current) &&
                                assoc.ThisMember.StorageAccessor.GetBoxedValue(Current) == null) { 
                                return true;
                            }
                        }
                    } 
                    return false;
                } 
 
                internal override bool IsInteresting {
                    get { 
                        return this.state == State.New ||
                               this.state == State.Deleted ||
                               this.state == State.Modified ||
                               (this.state == State.PossiblyModified && this.current != this.original) || 
                               CanInferDelete();
                    } 
                } 

                internal override void ConvertToNew() { 
                    // must be new or unmodified or removed to convert to new
                    System.Diagnostics.Debug.Assert(this.IsNew || this.IsRemoved || this.IsUnmodified);
                    this.original = null;
                    this.state = State.New; 
                }
 
                internal override void ConvertToPossiblyModified() { 
                    System.Diagnostics.Debug.Assert(this.IsPossiblyModified || this.IsDeleted);
                    this.state = State.PossiblyModified; 
                    this.isWeaklyTracked = false;
                }

                internal override void ConvertToModified() { 
                    System.Diagnostics.Debug.Assert(this.IsPossiblyModified);
                    System.Diagnostics.Debug.Assert(this.type.VersionMember != null || !this.type.HasUpdateCheck); 
                    this.state = State.Modified; 
                    this.isWeaklyTracked = false;
                } 

                internal override void ConvertToPossiblyModified(object originalState) {
                    // must be modified or unmodified to convert to modified
                    System.Diagnostics.Debug.Assert(this.IsNew || this.IsPossiblyModified); 
                    System.Diagnostics.Debug.Assert(originalState != null);
                    System.Diagnostics.Debug.Assert(originalState.GetType() == this.type.Type); 
                    this.state = State.PossiblyModified; 
                    this.original = this.CreateDataCopy(originalState);
                    this.isWeaklyTracked = false; 
                }

                internal override void ConvertToDeleted() {
                    // must be modified or unmodified to be deleted 
                    System.Diagnostics.Debug.Assert(this.IsDeleted || this.IsPossiblyModified);
                    this.state = State.Deleted; 
                    this.isWeaklyTracked = false; 
                }
 
                internal override void ConvertToDead() {
                    System.Diagnostics.Debug.Assert(this.IsDead || this.IsDeleted);
                    this.state = State.Dead;
                    this.isWeaklyTracked = false; 
                }
 
                internal override void ConvertToRemoved() { 
                    System.Diagnostics.Debug.Assert(this.IsRemoved || this.IsNew);
                    this.state = State.Removed; 
                    this.isWeaklyTracked = false;
                }

                internal override void ConvertToUnmodified() { 
                    System.Diagnostics.Debug.Assert(this.IsNew || this.IsPossiblyModified);
                    // reset to unmodified 
                    this.state = State.PossiblyModified; 
                    if (this.current is INotifyPropertyChanging) {
                        this.original = this.current; 
                    }
                    else {
                        this.original = this.CreateDataCopy(this.current);
                    } 
                    this.ResetDirtyMemberTracking();
                    this.isWeaklyTracked = false; 
                } 

                internal override void AcceptChanges() { 
                    if (IsWeaklyTracked) {
                        InitializeDeferredLoaders();
                        isWeaklyTracked = false;
                    } 
                    if (this.IsDeleted) {
                        this.ConvertToDead(); 
                    } 
                    else if (this.IsNew) {
                        this.InitializeDeferredLoaders(); 
                        this.ConvertToUnmodified();
                    }
                    else if (this.IsPossiblyModified) {
                        this.ConvertToUnmodified(); 
                    }
                } 
 
                private void AssignMember(object instance, MetaDataMember mm, object value) {
                    // In the unnotified case, directly use the storage accessor 
                    // for everything because there are not events to be fired.
                    if (!(this.current is INotifyPropertyChanging)) {
                        mm.StorageAccessor.SetBoxedValue(ref instance, value);
                    } 
                    else {
                        // Go through the member accessor to fire events. 
                        mm.MemberAccessor.SetBoxedValue(ref instance, value); 
                    }
                } 

                /// 
                /// Certain state is saved during change tracking to enable modifications
                /// to be detected taking refresh operations into account.  When changes 
                /// are reverted or accepted, this state must be reset.
                ///  
                private void ResetDirtyMemberTracking() { 
                    this.dirtyMemberCache.SetAll(false);
                } 

                /// 
                /// Refresh internal tracking state using the original value and mode
                /// specified. 
                /// 
                internal override void Refresh(RefreshMode mode, object freshInstance) { 
                    this.SynchDependentData(); 

                    // This must be done prior to updating original values 
                    this.UpdateDirtyMemberCache();

                    // Apply the refresh strategy to each data member
                    Type instanceType = freshInstance.GetType(); 
                    foreach (MetaDataMember mm in type.PersistentDataMembers) {
                        var memberMode = mm.IsDbGenerated ? RefreshMode.OverwriteCurrentValues : mode; 
                        if (memberMode != RefreshMode.KeepCurrentValues) { 
                            if (!mm.IsAssociation && (this.Type.Type == instanceType || mm.DeclaringType.Type.IsAssignableFrom(instanceType))) {
                                object freshValue = mm.StorageAccessor.GetBoxedValue(freshInstance); 
                                this.RefreshMember(mm, memberMode, freshValue);
                            }
                        }
                    } 

                    // Make the new data the current original value 
                    this.original = this.CreateDataCopy(freshInstance); 

                    if (mode == RefreshMode.OverwriteCurrentValues) { 
                        this.ResetDirtyMemberTracking();
                    }
                }
 
                /// 
                /// Using the last saved comparison baseline, figure out which members have 
                /// changed since the last refresh, and save that information.  This must be 
                /// done BEFORE any merge operations modify the current values.
                ///  
                private void UpdateDirtyMemberCache() {
                    // iterate over all members, and if they differ from
                    // last read values, mark as dirty
                    foreach (MetaDataMember mm in type.PersistentDataMembers) { 
                        if (mm.IsAssociation && mm.Association.IsMany) {
                            continue; 
                        } 
                        if (!this.dirtyMemberCache.Get(mm.Ordinal) && this.HasChangedValue(mm)) {
                            this.dirtyMemberCache.Set(mm.Ordinal, true); 
                        }
                    }
                }
 
                internal override void RefreshMember(MetaDataMember mm, RefreshMode mode, object freshValue) {
                    System.Diagnostics.Debug.Assert(!mm.IsAssociation); 
 
                    if (mode == RefreshMode.KeepCurrentValues) {
                        return; 
                    }

                    bool hasUserChange = this.HasChangedValue(mm);
 
                    // we don't want to overwrite any modified values, unless
                    // the mode is original wins 
                    if (hasUserChange && mode != RefreshMode.OverwriteCurrentValues) 
                        return;
 
                    object currentValue = mm.StorageAccessor.GetBoxedValue(this.current);
                    if (!object.Equals(freshValue, currentValue)) {
                        mm.StorageAccessor.SetBoxedValue(ref this.current, freshValue);
 
                        // update all singleton associations that are affected by a change to this member
                        foreach (MetaDataMember am in this.GetAssociationsForKey(mm)) { 
                            if (!am.Association.IsMany) { 
                                IEnumerable ds = this.tracker.services.GetDeferredSourceFactory(am).CreateDeferredSource(this.current);
                                if (am.StorageAccessor.HasValue(this.current)) { 
                                    this.AssignMember(this.current, am, ds.Cast().SingleOrDefault());
                                }
                            }
                        } 
                    }
                } 
 
                private IEnumerable GetAssociationsForKey(MetaDataMember key) {
                    foreach (MetaDataMember mm in this.type.PersistentDataMembers) { 
                        if (mm.IsAssociation && mm.Association.ThisKey.Contains(key)) {
                            yield return mm;
                        }
                    } 
                }
 
                internal override object CreateDataCopy(object instance) { 
                    System.Diagnostics.Debug.Assert(instance != null);
                    Type instanceType = instance.GetType(); 
                    System.Diagnostics.Debug.Assert(instance.GetType() == this.type.Type);

                    object copy = Activator.CreateInstance(this.Type.Type);
 
                    MetaType rootMetaType = this.tracker.services.Model.GetTable(instanceType).RowType.InheritanceRoot;
                    foreach (MetaDataMember mm in rootMetaType.GetInheritanceType(instanceType).PersistentDataMembers) { 
                        if (this.Type.Type != instanceType && !mm.DeclaringType.Type.IsAssignableFrom(instanceType)) { 
                            continue;
                        } 
                        if (mm.IsDeferred) {
                            // do not copy associations
                            if (!mm.IsAssociation) {
                                if (mm.StorageAccessor.HasValue(instance)) { 
                                    object value = mm.DeferredValueAccessor.GetBoxedValue(instance);
                                    mm.DeferredValueAccessor.SetBoxedValue(ref copy, value); 
                                } 
                                else {
                                    IEnumerable ds = this.tracker.services.GetDeferredSourceFactory(mm).CreateDeferredSource(copy); 
                                    mm.DeferredSourceAccessor.SetBoxedValue(ref copy, ds);
                                }
                            }
                        } 
                        else {
                            // otherwise assign the value as-is to the backup instance 
                            object value = mm.StorageAccessor.GetBoxedValue(instance); 
                            // assumes member values are immutable or will communicate changes to entity
                            // note: byte[] and char[] don't do this. 
                            mm.StorageAccessor.SetBoxedValue(ref copy, value);
                        }
                    }
                    return copy; 
                }
 
                internal void StartTracking() { 
                    if (this.original == this.current) {
                        this.original = this.CreateDataCopy(this.current); 
                    }
                }

                // Return value indicates whether or not any data was actually [....]'d 
                internal override bool SynchDependentData() {
                    bool valueWasSet = false; 
 
                    // set foreign key fields
                    foreach (MetaAssociation assoc in this.Type.Associations) { 
                        MetaDataMember mm = assoc.ThisMember;
                        if (assoc.IsForeignKey) {
                            bool hasAssigned = mm.StorageAccessor.HasAssignedValue(this.current);
                            bool hasLoaded = mm.StorageAccessor.HasLoadedValue(this.current); 
                            if (hasAssigned || hasLoaded) {
                                object parent = mm.StorageAccessor.GetBoxedValue(this.current); 
                                if (parent != null) { 
                                    // copy parent's current primary key into this instance's foreign key fields
                                    for (int i = 0, n = assoc.ThisKey.Count; i < n; i++) { 
                                        MetaDataMember accThis = assoc.ThisKey[i];
                                        MetaDataMember accParent = assoc.OtherKey[i];
                                        object parentValue = accParent.StorageAccessor.GetBoxedValue(parent);
                                        accThis.StorageAccessor.SetBoxedValue(ref this.current, parentValue); 
                                        valueWasSet = true;
                                    } 
                                } 
                                else if (assoc.IsNullable) {
                                    if (mm.IsDeferred || (this.original != null && mm.MemberAccessor.GetBoxedValue(this.original) != null)) { 
                                        // no known parent? set to null
                                        for (int i = 0, n = assoc.ThisKey.Count; i < n; i++) {
                                            MetaDataMember accThis = assoc.ThisKey[i];
                                            if (accThis.CanBeNull) { 
                                                if (this.original != null && this.HasChangedValue(accThis)) {
                                                    if (accThis.StorageAccessor.GetBoxedValue(this.current) != null) { 
                                                        throw Error.InconsistentAssociationAndKeyChange(accThis.Member.Name, mm.Member.Name); 
                                                    }
                                                } 
                                                else {
                                                    accThis.StorageAccessor.SetBoxedValue(ref this.current, null);
                                                    valueWasSet = true;
                                                } 
                                            }
                                        } 
                                    } 
                                }
                                else if (!hasLoaded) { 
                                    //Else the parent association has been set to null; but the ID is not nullable so
                                    //the value can not be set
                                    StringBuilder keys = new StringBuilder();
                                    foreach (MetaDataMember key in assoc.ThisKey) { 
                                        if (keys.Length > 0) {
                                            keys.Append(", "); 
                                        } 
                                        keys.AppendFormat("{0}.{1}", this.Type.Name.ToString(), key.Name);
                                    } 
                                    throw Error.CouldNotRemoveRelationshipBecauseOneSideCannotBeNull(assoc.OtherType.Name, this.Type.Name, keys);
                                }
                            }
                        } 
                    }
 
                    /// Explicitly set any inheritance discriminator for item. 
                    if (this.type.HasInheritance) {
                        if (this.original != null) { 
                            object currentDiscriminator = type.Discriminator.MemberAccessor.GetBoxedValue(this.current);
                            MetaType currentTypeFromDiscriminator = TypeFromDiscriminator(this.type, currentDiscriminator);
                            object dbDiscriminator = type.Discriminator.MemberAccessor.GetBoxedValue(this.original);
                            MetaType dbTypeFromDiscriminator = TypeFromDiscriminator(this.type, dbDiscriminator); 

                            // Would the discriminator change also change the type? If so, its not allowed. 
                            if (currentTypeFromDiscriminator != dbTypeFromDiscriminator) { 
                                throw Error.CannotChangeInheritanceType(dbDiscriminator,
                                    currentDiscriminator, original.GetType().Name, currentTypeFromDiscriminator); 
                            }
                        }
                        else {
                            // No db value means this is an 'Add'. Set the discriminator. 
                            MetaType currentType = type.GetInheritanceType(this.current.GetType());
                            if (currentType.HasInheritanceCode) { 
                                object code = currentType.InheritanceCode; 
                                this.type.Discriminator.MemberAccessor.SetBoxedValue(ref current, code);
                                valueWasSet = true; 
                            }
                        }
                    }
                    return valueWasSet; 
                }
 
                internal override bool HasChangedValue(MetaDataMember mm) { 
                    if (this.current == this.original) {
                        return false; 
                    }
                    if (mm.IsAssociation && mm.Association.IsMany) {
                        return mm.StorageAccessor.HasAssignedValue(this.original);
                    } 
                    if (mm.StorageAccessor.HasValue(this.current)) {
                        if (this.original != null && mm.StorageAccessor.HasValue(this.original)) { 
                            // If the member has ever been in a modified state 
                            // in the past, it is considered modified
                            if (dirtyMemberCache.Get(mm.Ordinal)) { 
                                return true;
                            }
                            object baseline = mm.MemberAccessor.GetBoxedValue(this.original);
                            object currentValue = mm.MemberAccessor.GetBoxedValue(this.current); 
                            if (!object.Equals(currentValue, baseline)) {
                                return true; 
                            } 
                            return false;
                        } 
                        else if (mm.IsDeferred && mm.StorageAccessor.HasAssignedValue(this.current)) {
                            return true;
                        }
                    } 
                    return false;
                } 
 
                internal override bool HasChangedValues() {
                    if (this.current == this.original) { 
                        return false;
                    }
                    if (this.IsNew) {
                        return true; 
                    }
                    foreach (MetaDataMember mm in this.type.PersistentDataMembers) { 
                        if (!mm.IsAssociation && this.HasChangedValue(mm)) { 
                            return true;
                        } 
                    }
                    return false;
                }
 
                internal override IEnumerable GetModifiedMembers() {
                    foreach (MetaDataMember mm in this.type.PersistentDataMembers) { 
                        if (this.IsModifiedMember(mm)) { 
                            object currentValue = mm.MemberAccessor.GetBoxedValue(this.current);
                            if (this.original != null && mm.StorageAccessor.HasValue(this.original)) { 
                                object originalValue = mm.MemberAccessor.GetBoxedValue(this.original);
                                yield return new ModifiedMemberInfo(mm.Member, currentValue, originalValue);
                            }
                            else if (this.original == null || (mm.IsDeferred && !mm.StorageAccessor.HasLoadedValue(this.current))) { 
                                yield return new ModifiedMemberInfo(mm.Member, currentValue, null);
                            } 
                        } 
                    }
                } 

                private bool IsModifiedMember(MetaDataMember member) {
                    return !member.IsAssociation &&
                           !member.IsPrimaryKey && 
                           !member.IsVersion &&
                           !member.IsDbGenerated && 
                            member.StorageAccessor.HasAssignedValue(this.current) && 
                           (this.state == State.Modified ||
                           (this.state == State.PossiblyModified && this.HasChangedValue(member))); 
                }

                internal override bool HasDeferredLoaders {
                    get { 
                        foreach (MetaAssociation assoc in this.Type.Associations) {
                            if (HasDeferredLoader(assoc.ThisMember)) { 
                                return true; 
                            }
                        } 
                        IEnumerable deferredMembers = this.Type.PersistentDataMembers.Where(p => p.IsDeferred && !p.IsAssociation);
                        foreach (MetaDataMember deferredMember in deferredMembers) {
                            if (HasDeferredLoader(deferredMember)) {
                                return true; 
                            }
                        } 
                        return false; 
                    }
                } 

                private bool HasDeferredLoader(MetaDataMember deferredMember) {
                    if (!deferredMember.IsDeferred) {
                        return false; 
                    }
 
                    MetaAccessor acc = deferredMember.StorageAccessor; 
                    if (acc.HasAssignedValue(this.current) || acc.HasLoadedValue(this.current)) {
                        return false; 
                    }
                    MetaAccessor dsacc = deferredMember.DeferredSourceAccessor;
                    IEnumerable loader = (IEnumerable)dsacc.GetBoxedValue(this.current);
 
                    return loader != null;
                } 
 
                /// 
                /// Called to initialize deferred loaders for New or Attached entities. 
                /// 
                internal override void InitializeDeferredLoaders() {
                    if (this.tracker.services.Context.DeferredLoadingEnabled) {
                        foreach (MetaAssociation assoc in this.Type.Associations) { 
                            // don't set loader on association that is dependent on unrealized generated values
                            if (!this.IsPendingGeneration(assoc.ThisKey)) { 
                                InitializeDeferredLoader(assoc.ThisMember); 
                            }
                        } 
                        IEnumerable deferredMembers = this.Type.PersistentDataMembers.Where(p => p.IsDeferred && !p.IsAssociation);
                        foreach (MetaDataMember deferredMember in deferredMembers) {
                            // don't set loader on member that is dependent on unrealized generated values
                            if (!this.IsPendingGeneration(Type.IdentityMembers)) { 
                                InitializeDeferredLoader(deferredMember);
                            } 
                        } 
                        haveInitializedDeferredLoaders = true;
                    } 
                }

                private void InitializeDeferredLoader(MetaDataMember deferredMember) {
                    MetaAccessor acc = deferredMember.StorageAccessor; 
                    if (!acc.HasAssignedValue(this.current) && !acc.HasLoadedValue(this.current)) {
                        MetaAccessor dsacc = deferredMember.DeferredSourceAccessor; 
                        IEnumerable loader = (IEnumerable)dsacc.GetBoxedValue(this.current); 
                        // don't reset loader on any deferred member that already has one
                        if (loader == null) { 
                            IDeferredSourceFactory factory = this.tracker.services.GetDeferredSourceFactory(deferredMember);
                            loader = factory.CreateDeferredSource(this.current);
                            dsacc.SetBoxedValue(ref this.current, loader);
 
                        }
                        else if (loader != null && !haveInitializedDeferredLoaders) { 
                            // If loader is present but wasn't generated by us, then 
                            // an attempt to Attach or Add an entity from another context
                            // has been made, which is not supported. 
                            throw Error.CannotAttachAddNonNewEntities();
                        }
                    }
                } 

                internal override bool IsPendingGeneration(IEnumerable key) { 
                    if (this.IsNew) { 
                        foreach (MetaDataMember member in key) {
                            if (IsMemberPendingGeneration(member)) { 
                                return true;
                            }
                        }
                    } 
                    return false;
                } 
 
                internal override bool IsMemberPendingGeneration(MetaDataMember keyMember) {
                    if (this.IsNew && keyMember.IsDbGenerated) { 
                        return true;
                    }
                    // look for any FK association that has this key member (should only be one)
                    foreach (MetaAssociation assoc in type.Associations) { 
                        if (assoc.IsForeignKey) {
                            int index = assoc.ThisKey.IndexOf(keyMember); 
                            if (index > -1) { 
                                // we must have a reference to this other object to know if its side of
                                // the association is generated or not 
                                object otherItem = null;
                                if (assoc.ThisMember.IsDeferred) {
                                    otherItem = assoc.ThisMember.DeferredValueAccessor.GetBoxedValue(this.current);
                                } 
                                else {
                                    otherItem = assoc.ThisMember.StorageAccessor.GetBoxedValue(this.current); 
                                } 
                                if (otherItem != null) {
                                    if (assoc.IsMany) { 
                                        // Can't be pending generation for a value that would have to be the same
                                        // across many rows.
                                        continue;
                                    } 
                                    else {
                                        StandardTrackedObject trackedOther = (StandardTrackedObject)this.tracker.GetTrackedObject(otherItem); 
                                        if (trackedOther != null) { 
                                            MetaDataMember otherMember = assoc.OtherKey[index];
                                            return trackedOther.IsMemberPendingGeneration(otherMember); 
                                        }
                                    }
                                }
                            } 
                        }
                    } 
                    return false; 
                }
            } 
        }

        /// 
        /// This is the implementation used when change tracking is disabled. 
        /// 
        class ReadOnlyChangeTracker : ChangeTracker { 
            internal override TrackedObject Track(object obj) { return null; } 
            internal override TrackedObject Track(object obj, bool recurse) { return null; }
            internal override void FastTrack(object obj) { } 
            internal override bool IsTracked(object obj) { return false; }
            internal override TrackedObject GetTrackedObject(object obj) { return null; }
            internal override void StopTracking(object obj) { }
            internal override void AcceptChanges() { } 
            internal override IEnumerable GetInterestingObjects() { return new TrackedObject[0]; }
        } 
    } 

    internal abstract class TrackedObject { 
        internal abstract MetaType Type { get; }
        /// 
        /// The current client value.
        ///  
        internal abstract object Current { get; }
        ///  
        /// The last read database value.  This is updated whenever the 
        /// item is refreshed.
        ///  
        internal abstract object Original { get; }
        internal abstract bool IsInteresting { get; } // new, deleted or possibly changed
        internal abstract bool IsNew { get; }
        internal abstract bool IsDeleted { get; } 
        internal abstract bool IsModified { get; }
        internal abstract bool IsUnmodified { get; } 
        internal abstract bool IsPossiblyModified { get; } 
        internal abstract bool IsRemoved { get; }
        internal abstract bool IsDead { get; } 
        /// 
        /// True if the object is being tracked (perhaps during a recursive
        /// attach operation) but can be transitioned to other states.
        ///  
        internal abstract bool IsWeaklyTracked { get; }
        internal abstract bool HasDeferredLoaders { get; } 
        internal abstract bool HasChangedValues(); 
        internal abstract IEnumerable GetModifiedMembers();
        internal abstract bool HasChangedValue(MetaDataMember mm); 
        internal abstract bool CanInferDelete();
        internal abstract void AcceptChanges();
        internal abstract void ConvertToNew();
        internal abstract void ConvertToPossiblyModified(); 
        internal abstract void ConvertToPossiblyModified(object original);
        internal abstract void ConvertToUnmodified(); 
        internal abstract void ConvertToModified(); 
        internal abstract void ConvertToDeleted();
        internal abstract void ConvertToRemoved(); 
        internal abstract void ConvertToDead();
        /// 
        /// Refresh the item by making the value passed in the current
        /// Database value, and refreshing the current values using the 
        /// mode specified.
        ///  
        internal abstract void Refresh(RefreshMode mode, object freshInstance); 
        /// 
        /// Does the refresh operation for a single member.  This method does not 
        /// update the baseline 'original' value.  You must call
        /// Refresh(RefreshMode.KeepCurrentValues, freshInstance) to finish the refresh
        /// after refreshing individual members.
        ///  
        /// 
        ///  
        ///  
        internal abstract void RefreshMember(MetaDataMember member, RefreshMode mode, object freshValue);
        ///  
        /// Create a data-member only copy of the instance (no associations)
        /// 
        /// 
        internal abstract object CreateDataCopy(object instance); 

        internal abstract bool SynchDependentData(); 
 
        internal abstract bool IsPendingGeneration(IEnumerable keyMembers);
        internal abstract bool IsMemberPendingGeneration(MetaDataMember keyMember); 

        internal abstract void InitializeDeferredLoaders();
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.

                        

                        

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