ObjectStateEntry.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / ndp / fx / src / DataEntity / System / Data / Objects / ObjectStateEntry.cs / 2 / ObjectStateEntry.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner       [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 
using System;
using System.Collections; 
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Common; 
using System.Data.Metadata.Edm;
using System.Data.Mapping; 
using System.Data.Objects.DataClasses; 
using System.Diagnostics;
using System.Reflection; 
using System.Collections.Specialized;

namespace System.Data.Objects
{ 
    internal sealed class EntityEntry : ObjectStateEntry
    { 
#if DEBUG 
        private static int _objectTypeCount; // Bid counter
        internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); 
#endif

        #region RelationshipEnd fields
        ///  
        /// Singlely-linked list of RelationshipEntry.
        /// One of the ends in the RelationshipEntry must equal this.EntityKey 
        ///  
        private RelationshipEntry _headRelationshipEnds;
 
        /// 
        /// Number of RelationshipEntry in the _relationshipEnds list.
        /// 
        private int _countRelationshipEnds; 
        #endregion
 
        #region constructors 
        internal EntityEntry(object entity, EntityKey entityKey, EntitySet extent, ObjectStateManager cache,
            StateManagerTypeMetadata typeMetadata, bool isAdded) 
            : base(entity, entityKey, extent, cache, typeMetadata, isAdded)
        {
        }
 
        // entity stub
        internal EntityEntry(EntityKey entityKey, EntitySet extent, ObjectStateManager cache, StateManagerTypeMetadata typeMetadata) 
            : base(entityKey, extent, cache, typeMetadata) 
        {
        } 
        #endregion

        #region RelationshipEnd methods
 
        /// 
        /// Add a RelationshipEntry (one of its ends must equal this.EntityKey) 
        ///  
        internal void AddRelationshipEnd(RelationshipEntry item)
        { 
#if DEBUG
            Debug.Assert(null != item, "null item");
            Debug.Assert(null != item.Wrapper, "null RelationshipWrapper");
            Debug.Assert(0 <= _countRelationshipEnds, "negative _relationshipEndCount"); 
            Debug.Assert(EntityKey.Equals(item.Wrapper.Key0) || EntityKey.Equals(item.Wrapper.Key1), "entity key doesn't match");
 
            for (RelationshipEntry current = _headRelationshipEnds; 
                 null != current;
                 current = current.GetNextRelationshipEnd(EntityKey)) 
            {
                Debug.Assert(!Object.ReferenceEquals(item, current), "RelationshipEntry already in list");
                Debug.Assert(!item.Wrapper.Equals(current.Wrapper), "RelationshipWrapper already in list");
            } 
#endif
            // the item will become the head of the list 
            // i.e. you walk the list in reverse order of items being added 
            item.SetNextRelationshipEnd(this.EntityKey, _headRelationshipEnds);
            _headRelationshipEnds = item; 
            _countRelationshipEnds++;

            Debug.Assert(_countRelationshipEnds == (new RelationshipEndEnumerable(this)).ToArray().Length, "different count");
        } 

        ///  
        /// Determines if a given relationship entry is present in the list of entries 
        /// 
        /// The entry to look for 
        /// True of the relationship end is found
        internal bool ContainsRelationshipEnd(RelationshipEntry item)
        {
            for (RelationshipEntry current = _headRelationshipEnds; 
                 null != current;
                 current = current.GetNextRelationshipEnd(EntityKey)) 
            { 
                if (object.ReferenceEquals(current, item))
                { 
                    return true;
                }
            }
            return false; 
        }
 
 
        /// 
        /// Remove a RelationshipEntry (one of its ends must equal this.EntityKey) 
        /// 
        /// 
        internal void RemoveRelationshipEnd(RelationshipEntry item)
        { 
            Debug.Assert(null != item, "removing null");
            Debug.Assert(null != item.Wrapper, "null RelationshipWrapper"); 
            Debug.Assert(1 <= _countRelationshipEnds, "negative _relationshipEndCount"); 
            Debug.Assert(EntityKey.Equals(item.Wrapper.Key0) || EntityKey.Equals(item.Wrapper.Key1), "entity key doesn't match");
 
            // walk the singly-linked list, remembering the previous node so we can remove the current node
            for (RelationshipEntry current = _headRelationshipEnds, previous = null;
                 null != current;
                 previous = current, current = current.GetNextRelationshipEnd(EntityKey)) 
            {
                if (Object.ReferenceEquals(item, current)) 
                { 
                    if (this.EntityKey.Equals(current.Key0))
                    {   // if this.EntityKey matches Key0, NextKey0 is the next element in the lsit 
                        if (null == previous)
                        {
                            _headRelationshipEnds = current.NextKey0;
                        } 
                        else
                        { 
                            previous.SetNextRelationshipEnd(EntityKey, current.NextKey0); 
                        }
                        current.NextKey0 = null; 
                    }
                    else
                    {   // if this.EntityKey matches Key1, NextKey1 is the next element in the lsit
                        Debug.Assert(EntityKey.Equals(current.Wrapper.Key1), "entity key didn't match"); 
                        if (null == previous)
                        { 
                            _headRelationshipEnds = current.NextKey1; 
                        }
                        else 
                        {
                            previous.SetNextRelationshipEnd(EntityKey, current.NextKey1);
                        }
                        current.NextKey1 = null; 
                    }
                    --_countRelationshipEnds; 
 
                    Debug.Assert(_countRelationshipEnds == (new RelationshipEndEnumerable(this)).ToArray().Length, "different count");
                    return; 
                }
                Debug.Assert(!item.Wrapper.Equals(current.Wrapper), "same wrapper, different RelationshipEntry instances");
            }
            Debug.Assert(false, "didn't remove a RelationshipEntry"); 
        }
 
        ///  
        /// Update one of the ends for the related RelationshipEntry
        ///  
        /// the EntityKey the relationship should currently have
        /// if promoting entity stub to full entity
        internal void UpdateRelationshipEnds(EntityKey oldKey, EntityEntry promotedEntry)
        { 
            Debug.Assert(null != (object)oldKey && oldKey.IsTemporary, "bad oldKey");
            Debug.Assert(!EntityKey.IsTemporary, "bad newKey, should not be temporary"); 
            Debug.Assert(!Object.ReferenceEquals(this, promotedEntry), "shouldn't be same reference"); 

            // traverse the list to update one of the ends in the relationship entry 
            int count = 0;
            RelationshipEntry next = _headRelationshipEnds;
            while (null != next)
            { 
                // get the next relationship end before we change the key of current relationship end
                RelationshipEntry current = next; 
                next = next.GetNextRelationshipEnd(oldKey); 

                // update the RelationshipEntry from the temporary key to real key 
                current.ChangeRelatedEnd(oldKey, EntityKey);

                // If we have a promoted entry, copy the relationship entries to the promoted entry
                // only if the promoted entry doesn't already know about that particular relationship entry 
                // This can be the case with self referencing entities
                if (null != promotedEntry && !promotedEntry.ContainsRelationshipEnd(current)) 
                {   // all relationship ends moved to new promotedEntry 
                    promotedEntry.AddRelationshipEnd(current);
                } 
                ++count;
            }
            Debug.Assert(count == _countRelationshipEnds, "didn't traverse all relationships");
            if (null != promotedEntry) 
            {   // cleanup existing (dead) entry to reduce confusion
                _headRelationshipEnds = null; 
                _countRelationshipEnds = 0; 
            }
        } 

        #region Enumerable and Enumerator
        internal RelationshipEndEnumerable GetRelationshipEnds()
        { 
            return new RelationshipEndEnumerable(this);
        } 
 
        /// 
        /// An enumerable so that EntityEntry doesn't implement it 
        /// 
        internal struct RelationshipEndEnumerable : IEnumerable, IEnumerable
        {
            internal static readonly RelationshipEntry[] EmptyRelationshipEntryArray = new RelationshipEntry[0]; 
            private readonly EntityEntry _entityEntry;
 
            internal RelationshipEndEnumerable(EntityEntry entityEntry) 
            {   // its okay if entityEntry is null
                _entityEntry = entityEntry; 
            }
            public RelationshipEndEnumerator GetEnumerator()
            {
                return new RelationshipEndEnumerator(_entityEntry); 
            }
            IEnumerator IEnumerable.GetEnumerator() 
            { 
                return GetEnumerator();
            } 
            IEnumerator IEnumerable.GetEnumerator()
            {
                Debug.Assert(false, "dead code, don't box the RelationshipEndEnumerable");
                return GetEnumerator(); 
            }
            IEnumerator IEnumerable.GetEnumerator() 
            { 
                Debug.Assert(false, "dead code, don't box the RelationshipEndEnumerable");
                return GetEnumerator(); 
            }

            /// 
            /// Convert the singly-linked list into an Array 
            /// 
            internal RelationshipEntry[] ToArray() 
            { 
                RelationshipEntry[] list = null;
                if ((null != _entityEntry) && (0 < _entityEntry._countRelationshipEnds)) 
                {
                    RelationshipEntry relationshipEnd = _entityEntry._headRelationshipEnds;
                    list = new RelationshipEntry[_entityEntry._countRelationshipEnds];
                    for (int i = 0; i < list.Length; ++i) 
                    {
                        Debug.Assert(null != relationshipEnd, "count larger than list"); 
                        Debug.Assert(_entityEntry.EntityKey.Equals(relationshipEnd.Key0) || _entityEntry.EntityKey.Equals(relationshipEnd.Key1), "entity key mismatch"); 
                        list[i] = relationshipEnd;
 
                        relationshipEnd = relationshipEnd.GetNextRelationshipEnd(_entityEntry.EntityKey);
                    }
                    Debug.Assert(null == relationshipEnd, "count smaller than list");
                } 
                return list ?? EmptyRelationshipEntryArray;
            } 
        } 

        ///  
        /// An enumerator to walk the RelationshipEntry linked-list
        /// 
        internal struct RelationshipEndEnumerator : IEnumerator, IEnumerator
        { 
            private readonly EntityEntry _entityEntry;
            private RelationshipEntry _current; 
 
            internal RelationshipEndEnumerator(EntityEntry entityEntry)
            { 
                _entityEntry = entityEntry;
                _current = null;
            }
            public RelationshipEntry Current 
            {
                get { return _current; } 
            } 
            IEntityStateEntry IEnumerator.Current
            { 
                get { return _current; }
            }
            object IEnumerator.Current
            { 
                get
                { 
                    Debug.Assert(false, "dead code, don't box the RelationshipEndEnumerator"); 
                    return _current;
                } 
            }
            public void Dispose()
            {
            } 
            public bool MoveNext()
            { 
                if (null != _entityEntry) 
                {
                    if (null == _current) 
                    {
                        _current = _entityEntry._headRelationshipEnds;
                    }
                    else 
                    {
                        _current = _current.GetNextRelationshipEnd(_entityEntry.EntityKey); 
                    } 
                }
                return (null != _current); 
            }
            public void Reset()
            {
                Debug.Assert(false, "not implemented"); 
            }
        } 
        #endregion 
        #endregion
    } 

    internal sealed class RelationshipEntry : ObjectStateEntry
    {
#if DEBUG 
        private static int _objectTypeCount; // Bid counter
        internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); 
#endif 

        #region Linked list of related relationships 
        private RelationshipEntry _nextKey0;
        private RelationshipEntry _nextKey1;
        #endregion
 
        // relationship
        internal RelationshipEntry(ObjectStateManager cache, EntityState state, RelationshipWrapper wrapper) 
            : base(cache, state, wrapper) 
        {
        } 

        #region RelationshipEnds as singly-linked list

        internal EntityKey Key0 { get { return Wrapper.Key0; } } 
        internal EntityKey Key1 { get { return Wrapper.Key1; } }
 
        ///  
        /// Update one of the ends of the relationship
        ///  
        internal void ChangeRelatedEnd(EntityKey oldKey, EntityKey newKey)
        {
            if (oldKey.Equals(Key0))
            { 
                if (oldKey.Equals(Key1))
                {   // self-reference 
                    Wrapper = new RelationshipWrapper(Wrapper.AssociationSet, newKey); 
                }
                else 
                {
                    Wrapper = new RelationshipWrapper(Wrapper, 0, newKey);
                }
            } 
            else
            { 
                Wrapper = new RelationshipWrapper(Wrapper, 1, newKey); 
            }
        } 

        internal RelationshipEntry GetNextRelationshipEnd(EntityKey entityKey)
        {
            Debug.Assert(null != (object)entityKey, "null EntityKey"); 
            Debug.Assert(entityKey.Equals(Key0) || entityKey.Equals(Key1), "EntityKey mismatch");
            return (entityKey.Equals(Key0) ? NextKey0 : NextKey1); 
        } 

        internal void SetNextRelationshipEnd(EntityKey entityKey, RelationshipEntry nextEnd) 
        {
            Debug.Assert(null != (object)entityKey, "null EntityKey");
            Debug.Assert(entityKey.Equals(Key0) || entityKey.Equals(Key1), "EntityKey mismatch");
            if (entityKey.Equals(Key0)) 
            {
                NextKey0 = nextEnd; 
            } 
            else
            { 
                NextKey1 = nextEnd;
            }
        }
 
        /// 
        /// Use when EntityEntry.EntityKey == this.Wrapper.Key0 
        ///  
        internal RelationshipEntry NextKey0
        { 
            get { return _nextKey0; }
            set { _nextKey0 = value; }

        } 

        ///  
        /// Use when EntityEntry.EntityKey == this.Wrapper.Key1 
        /// 
        internal RelationshipEntry NextKey1 
        {
            get { return _nextKey1; }
            set { _nextKey1 = value; }
        } 
        #endregion
    } 
 
    // Detached - nothing
 
    // Added - _entity & _currentValues only for shadowState

    // Unchanged - _entity & _currentValues only for shadowState
    // Unchanged -> Deleted - _entity & _currentValues only for shadowState 

    // Modified - _currentValues & _modifiedFields + _originalValues only on change 
    // Modified -> Deleted - _currentValues & _modifiedFields + _originalValues only on change 

    ///  
    /// Represets either a entity, entity stub or relationship
    /// 
    public abstract class ObjectStateEntry : IEntityStateEntry, IEntityChangeTracker
    { 
        #region common entry fields
        private ObjectStateManager _cache; 
        private EntityState _state; 
        #endregion
 
        #region entity entry fields
        private StateManagerTypeMetadata _cacheTypeMetadata;
        private EntitySetBase _entitySet;   // !null if IsKeyEntry or Entity (or IsRelationship)
        private EntityKey _entityKey;       // !null if IsKeyEntry or Entity 
        private object _entity;             // null if IsKeyEntry
 
        // entity entry change tracking 
        private BitArray _modifiedFields;  // only and always exists if state is Modified or after Delete() on Modified
        private List _originalValues; // only exists if _modifiedFields has a true-bit 
        #endregion

        #region relationship entry fields
        private RelationshipWrapper _wrapper;   // !null if IsRelationship 
        #endregion
 
        // ObjectStateEntry will not be detached and creation will be handled from ObjectStateManager 
        internal ObjectStateEntry(object entity, EntityKey entityKey, EntitySet extent, ObjectStateManager cache,
            StateManagerTypeMetadata typeMetadata, bool isAdded) 
        {
            Debug.Assert(entity != null, "entity cannot be null.");
            Debug.Assert(extent != null, "extent cannot be null.");
            Debug.Assert(cache != null, "cache cannot be null."); 
            Debug.Assert(typeMetadata != null, "typeMetadata cannot be null.");
            Debug.Assert((null == (object)entityKey) || (entityKey.EntitySetName == extent.Name), "different extent"); 
 
            _cache = cache;
            _entity = entity; 
            _entitySet = extent;
            _cacheTypeMetadata = typeMetadata;
            _entityKey = entityKey;
            this.State = isAdded ? EntityState.Added : EntityState.Unchanged; 
        }
 
        // ObjectStateEntry for Entity Keys (i.e. Span Stubs) 
        internal ObjectStateEntry(EntityKey entityKey, EntitySet extent, ObjectStateManager cache, StateManagerTypeMetadata typeMetadata)
        { 
            Debug.Assert((object)entityKey != null, "entityKey cannot be null.");
            Debug.Assert(extent != null, "extent cannot be null.");
            Debug.Assert(cache != null, "cache cannot be null.");
            Debug.Assert(typeMetadata != null, "typeMetadata cannot be null."); 
            Debug.Assert(entityKey.EntitySetName == extent.Name, "different extent");
 
            _cache = cache; 
            _entityKey = entityKey;
            _entitySet = extent; 
            _cacheTypeMetadata = typeMetadata;

            State = EntityState.Unchanged;
        } 

        ///  
        /// for RelationshipEntry 
        /// 
        internal ObjectStateEntry(ObjectStateManager cache, EntityState state, RelationshipWrapper wrapper) 
        {
            Debug.Assert(null != cache, "null ObjectStateManager");
            Debug.Assert(EntityState.Added == state ||
                         EntityState.Unchanged == state || 
                         EntityState.Deleted == state,
                         "invalid EntityState"); 
            Debug.Assert(null != wrapper, "null RelationshipWrapper"); 
            _cache = cache;
            _entitySet = wrapper.AssociationSet; 
            _wrapper = wrapper;
            _state = state;
        }
 
        IEntityStateManager IEntityStateEntry.StateManager
        { 
            get 
            {
                return (IEntityStateManager)this.ObjectStateManager; 
            }
        }

        ///  
        /// ObjectStateManager property of ObjectStateEntry.
        ///  
        ///  
        ///  ObjectStateManager 
        public ObjectStateManager ObjectStateManager 
        {
            get
            {
                ValidateState(); 
                return _cache;
            } 
        } 
        /// 
        /// Entity property of ObjectStateEntry. 
        /// 
        /// 
        ///  The entity encapsulated by this entry. 
        public object Entity 
        {
            get 
            { 
                ValidateState();
                return _entity; 
            }
        }

        ///  
        /// The EntityKey associated with the ObjectStateEntry
        ///  
        public EntityKey EntityKey 
        {
            get 
            {
                ValidateState();
                return _entityKey;
            } 
            internal set
            { 
                _entityKey = value; 
            }
        } 

        ///  Extent property of ObjectStateEntry. 
        /// 
        ///  Extent  
        public EntitySetBase EntitySet
        { 
            get 
            {
                ValidateState(); 
                return _entitySet;
            }
        }
 
        /// 
        /// Determines if this ObjectStateEntry represents a relationship 
        ///  
        public bool IsRelationship
        { 
            get
            {
                Debug.Assert((null != _wrapper && null == _entity && null == (object)_entityKey) ||
                             (null == _wrapper && (null != _entity || null != (object)_entityKey)) || 
                             (EntityState.Detached == _state),
                             "IsEntity, IsKeyEntry, IsRelationship mixup"); 
 
                // wrapper is always reset to null when the entry is Detached, so if it's not null, this has to be a relationship entry
                // If the wrapper is null, it may be a different kind of entry or may be detached. For consistency with rest of public 
                // API, throw if it's Detached.
                if (null == _wrapper)
                {
                    ValidateState(); 
                    return false;
                } 
                else 
                {
                    return true; 
                }
            }
        }
 
        internal bool IsKeyEntry
        { 
            get 
            {
                Debug.Assert((null != _wrapper && null == _entity && null == (object)_entityKey) || 
                             (null == _wrapper && (null != _entity || null != (object)_entityKey)) ||
                             (EntityState.Detached == _state),
                             "IsEntity, IsKeyEntry, IsRelationship mixup");
                return ((null == _entity) && (null != (object)_entityKey)); // if there is no entity but there is a key, then it is a key entry 
            }
        } 
 
        // must explicitly implement this because interface is internal & so is the property on the
        // class itself -- apparently the compiler won't let anything marked as internal be part of 
        // an interface (even if the interface is also internal)
        bool IEntityStateEntry.IsKeyEntry
        {
            get 
            {
                return this.IsKeyEntry; 
            } 
        }
 
        internal int GetFieldCount(StateManagerTypeMetadata metadata)
        {
            if (!IsRelationship)
            { 
                return metadata.FieldCount;
            } 
            return _wrapper.AssociationEndMembers.Count; 
        }
 
        internal void PromoteKeyEntry(object entity, IExtendedDataRecord shadowValues, StateManagerTypeMetadata typeMetadata)
        {
            Debug.Assert(entity != null, "entity cannot be null.");
            Debug.Assert(IsKeyEntry, "ObjectStateEntry should be a key."); 
            Debug.Assert(typeMetadata != null, "typeMetadata cannot be null.");
 
            _entity = entity; 

            // Allow updating of cached metadata because the actual entity might be a derived type 
            _cacheTypeMetadata = typeMetadata;

#if DEBUG   // performance, don't do this work in retail until shadow state is supported
            if (shadowValues != null) 
            {
                // shadowState always  coms from materializer, just copy the shadowstate values 
                Debug.Assert(shadowValues.DataRecordInfo.RecordType.EdmType.Equals(_cacheTypeMetadata.CdmMetadata.EdmType), "different cspace metadata instance"); 
                for (int ordinal = 0; ordinal < _cacheTypeMetadata.FieldCount; ordinal++)
                { 
                    if (_cacheTypeMetadata.IsMemberPartofShadowState(ordinal))
                    {
                        Debug.Assert(false, "shadowstate not supported");
                    } 
                }
            } 
#endif 
        }
 
        /// 
        /// Turns this entry into a key entry (SPAN stub).
        /// 
        internal void DegradeEntry() 
        {
            Debug.Assert(!IsKeyEntry); 
            Debug.Assert(!IsRelationship); 
            Debug.Assert((object)_entityKey != null);
 
            _entityKey = EntityKey; //Performs validation.

            EntityUtil.SetChangeTrackerOntoEntity(_entity, (IEntityChangeTracker)null);
 
            _modifiedFields = null;
            _originalValues = null; 
 
            // we don't want temporary keys to exist outside of the context
            if (State == EntityState.Added) 
            {
                EntityUtil.SetKeyOntoEntity(_entity, null);
                _entityKey = null;
            } 

            if (State != EntityState.Unchanged) 
            { 
                _cache.ChangeState(this, this.State, EntityState.Unchanged);
                State = EntityState.Unchanged; 
            }

            _cache.RemoveEntryFromKeylessStore(_entity);
 
            RelationshipManager relationshipManager = EntityUtil.GetRelationshipManager(_entity);
            if (relationshipManager != null) 
            { 
                relationshipManager.DetachContext();
            } 

            object degradedEntity = _entity;
            _entity = null;
            _cache.OnObjectStateManagerChanged(CollectionChangeAction.Remove, degradedEntity); 

            Debug.Assert(IsKeyEntry); 
        } 

 

        /// 
        /// State property of ObjectStateEntry.
        ///  
        /// 
        ///  DataRowState  
        public EntityState State 
        {
            get 
            {
                return _state;
            }
            internal set 
            {
                _state = value; 
            } 
        }
 
        /// 
        /// Original values of entity
        /// 
        ///  
        ///  DbDataRecord 
        [DebuggerBrowsable(DebuggerBrowsableState.Never)] // don't have debugger view expand this 
        public DbDataRecord OriginalValues 
        {
            get 
            {
                ValidateState();
                if (_state == EntityState.Added)
                { 
                    throw EntityUtil.OriginalValuesDoesNotExist();
                } 
 
                if (IsRelationship)
                { 
                    return new ObjectStateEntryDbDataRecord(this);
                }
                else if (IsKeyEntry)
                { 
                    throw EntityUtil.CannotAccessKeyEntryValues();
                } 
                else 
                {
                    return new ObjectStateEntryDbDataRecord(this, _cacheTypeMetadata, _entity); 
                }
            }
        }
 
        [DebuggerBrowsable(DebuggerBrowsableState.Never)] // don't have debugger view expand this
        internal CurrentValueRecord EditableOriginalValues 
        { 
            get
            { 
                Debug.Assert(!IsRelationship, "should not edit original relationship");
                Debug.Assert(!IsKeyEntry, "should not edit original key entry");
                Debug.Assert(EntityState.Modified == State ||
                             EntityState.Deleted == State, 
                             "only expecting Modified or Deleted state");
 
                return new ObjectStateEntryOriginalDbUpdatableDataRecord(this, _cacheTypeMetadata, _entity); 
            }
        } 

        /// 
        /// Current values of entity/ DataRow
        ///  
        /// 
        ///  DbUpdatableDataRecord  
        [DebuggerBrowsable(DebuggerBrowsableState.Never)] // don't have debugger view expand this 
        public CurrentValueRecord CurrentValues
        { 
            get
            {
                ValidateState();
                if (_state == EntityState.Deleted) 
                {
                    throw EntityUtil.CurrentValuesDoesNotExist(); 
                } 

                if (IsRelationship) 
                {
                    return new ObjectStateEntryDbUpdatableDataRecord(this);
                }
                else if (IsKeyEntry) 
                {
                    throw EntityUtil.CannotAccessKeyEntryValues(); 
                } 
                else
                { 
                    return new ObjectStateEntryDbUpdatableDataRecord(this, _cacheTypeMetadata, _entity);
                }
            }
        } 

        internal RelationshipWrapper Wrapper 
        { 
            get
            { 
                return _wrapper;
            }
            set
            { 
                Debug.Assert(IsRelationship, "Non Relation ObjectStateEntry can not have RelationshipWrapper");
                Debug.Assert(null != value, "don't set wrapper to null"); 
                _wrapper = value; 
            }
        } 

        internal void AttachObjectStateManagerToEntity()
        {
            // This method should only be called in cases where we really have an entity to attach to 
            Debug.Assert(_entity != null, "Cannot attach a null entity to the state manager");
 
            EntityUtil.SetChangeTrackerOntoEntity(_entity, this); 
        }
 
        internal void DetachObjectStateManagerFromEntity()
        {
            // This method can be called on relationship entries where there is no entity
            if (!IsRelationship && !IsKeyEntry) // _entity is not null. 
            {
                EntityUtil.SetChangeTrackerOntoEntity(_entity, (IEntityChangeTracker)null); 
 
                RelationshipManager relationshipManager = EntityUtil.GetRelationshipManager(_entity);
                if (relationshipManager != null) 
                {
                    relationshipManager.DetachContext();
                }
 
                //Entry's this._entityKey is set to null at the caller, maintaining consistency between entityWithKey.EntityKey and this.EntityKey
                EntityUtil.SetKeyOntoEntity(_entity, null); 
            } 
        }
 
        internal Type GetFieldType(int ordinal, StateManagerTypeMetadata metadata)
        {
            if (!IsRelationship)
            { 
                return metadata.GetFieldType(ordinal);
            } 
            else 
            {
                return typeof(EntityKey); // this is given By Design 
            }
        }
        internal string GetCLayerName(int ordinal, StateManagerTypeMetadata metadata)
        { 
            if (!IsRelationship)
            { 
                return metadata.CLayerMemberName(ordinal); 
            }
            else 
            {
                ValidateRelationshipRange(ordinal);
                return _wrapper.AssociationEndMembers[ordinal].Name;
            } 
        }
        internal int GetOrdinalforCLayerName(string name, StateManagerTypeMetadata metadata) 
        { 
            if (!IsRelationship)
            { 
                return metadata.GetOrdinalforCLayerMemberName(name);
            }
            else
            { 
                AssociationEndMember endMember;
                ReadOnlyMetadataCollection endMembers = _wrapper.AssociationEndMembers; 
                if (endMembers.TryGetValue(name, false, out endMember)) 
                {
                    return endMembers.IndexOf(endMember); 
                }
                return -1;
            }
        } 

        ///  
        /// API to accept the current values as original values and  mark the entity as Unchanged. 
        /// 
        ///  
        /// 
        public void AcceptChanges()
        {
            ValidateState(); 

            Debug.Assert(!IsKeyEntry || State == EntityState.Unchanged, "Key ObjectStateEntries must always be unchanged."); 
 
            switch (State)
            { 
                case EntityState.Deleted:
                    CascadeAcceptChanges();
                    // Current entry could be already detached if this is relationship entry and if one end of relationship was a KeyEntry
                    if (_cache != null) 
                    {
                        _cache.ChangeState(this, EntityState.Deleted, EntityState.Detached); 
                    } 
                    break;
                case EntityState.Added: 
                    if (!IsRelationship)
                    {
                        // If this entry represents an entity, perform key fixup.
                        Debug.Assert(Entity != null, "Non-relationship entries should have a non-null entity."); 
                        Debug.Assert((object)_entityKey != null, "All entities in the state manager should have a non-null EntityKey.");
                        Debug.Assert(_entityKey.IsTemporary, "All entities in the Added state should have a temporary EntityKey."); 
 
                        // Retrieve referential constraint properties from Principal entities (possibly recursively)
                        // and check referential constraint properties in the Dependent entities (1 level only) 
                        this.RetrieveAndCheckReferentialConstraintValues();

                        _cache.FixupKey((EntityEntry)this);
                    } 
                    else
                    { 
                        _cache.ChangeState(this, EntityState.Added, EntityState.Unchanged); 
                    }
                    _modifiedFields = null; 
                    _originalValues = null;
                    State = EntityState.Unchanged;
                    break;
                case EntityState.Modified: 
                    _cache.ChangeState(this, EntityState.Modified, EntityState.Unchanged);
                    _modifiedFields = null; 
                    _originalValues = null; 
                    State = EntityState.Unchanged;
                    break; 
                case EntityState.Unchanged:
                    break;
            }
        } 

        private void CascadeAcceptChanges() 
        { 
            if (!IsRelationship)
            { 
                foreach (ObjectStateEntry entry in _cache.CopyOfRelationshipsByKey(EntityKey))
                {
                    Debug.Assert(entry.IsRelationship, "It should be a Relationship ObjectStateEntry");
 
                    // CascadeAcceptChanges is only called on Entity ObjectStateEntry when it is
                    // in deleted state. Entity is in deleted state therefore for all related Relationship 
                    // cache entries only valid state is Deleted. 
                    Debug.Assert(entry.State == EntityState.Deleted, "Relationship ObjectStateEntry should be in deleted state");
                    entry.AcceptChanges(); 
                }
            }
            else
            { 
                DeleteUnnecessaryKeyEntries();
            } 
        } 

        private void DeleteUnnecessaryKeyEntries() 
        {
            Debug.Assert(this.IsRelationship, "entry must be relationship entry");

            // We need to check to see if the ends of the relationship are key entries. 
            // If they are, and nothing else refers to them then the key entry should be removed.
            for (int i = 0; i < 2; i++) 
            { 
                EntityKey entityKey = this.GetCurrentRelationValue(i, false) as EntityKey;
                ObjectStateEntry relatedEntry = _cache.GetObjectStateEntry(entityKey); 
                if (relatedEntry.IsKeyEntry)
                {
                    bool foundRelationship = false;
                    // count the number of relationships this key entry is part of 
                    // if there aren't any, then the relationship should be deleted
                    foreach (ObjectStateEntry relationshipEntry in _cache.FindRelationshipsByKey(entityKey)) 
                    { 
                        // only count relationships that are not the one we are currently deleting (i.e. this)
                        if (relationshipEntry != this) 
                        {
                            foundRelationship = true;
                            break;
                        } 
                    }
                    if (!foundRelationship) 
                    { 
                        // Nothing is refering to this key entry, so it should be removed from the cache
                        _cache.DeleteKeyEntry(relatedEntry); 
                        // We assume that only one end of relationship can be a key entry,
                        // so we can break the loop
                        break;
                    } 
                }
            } 
        } 

        // Detaches related relationship entries if other ends of these relationships are key entries. 
        // Key entry from the other side of the relationship is removed if is not related to other entries.
        private void DetachRelationshipsEntries(RelationshipManager relationshipManager)
        {
            Debug.Assert(relationshipManager != null, "Unexpected null RelationshipManager"); 
            Debug.Assert(!this.IsKeyEntry, "Should only be detaching relationships with key entries if the source is not a key entry");
 
            foreach (ObjectStateEntry relationshipEntry in _cache.CopyOfRelationshipsByKey(EntityKey)) 
            {
                // Get state entry for other side of the relationship 
                EntityKey targetKey = relationshipEntry.Wrapper.GetOtherEntityKey(EntityKey);
                Debug.Assert((object)targetKey != null, "EntityKey not on either side of relationship as expected");

                ObjectStateEntry relatedEntry = _cache.GetObjectStateEntry(targetKey); 
                if (relatedEntry.IsKeyEntry)
                { 
                    // This must be an EntityReference, so set the DetachedEntityKey if the relationship is currently Added or Unchanged 
                    // devnote: This assumes that we are in the middle of detaching the entity associated with this state entry, because
                    //          we don't always want to preserve the EntityKey for every detached relationship, if the source entity itself isn't being detached 
                    if (relationshipEntry.State != EntityState.Deleted)
                    {
                        AssociationEndMember targetMember = relationshipEntry.Wrapper.GetAssociationEndMember(targetKey);
                        // devnote: Since we know the target end of this relationship is a key entry, it has to be a reference, so just cast 
                        EntityReference entityReference = (EntityReference) relationshipManager.GetRelatedEnd(targetMember.DeclaringType.FullName, targetMember.Name);
                        entityReference.DetachedEntityKey = targetKey; 
                    } 
                    // else do nothing -- we can't null out the key for Deleted state, because there could be other relationships with this same source in a different state
 
                    // Remove key entry if necessary
                    relationshipEntry.DeleteUnnecessaryKeyEntries();
                    // Remove relationship entry
                    relationshipEntry.DetachRelationshipEntry(); 
                }
                // else the other end is a real entity, we will deal with that separately 
            } 
        }
 

        // Retrieve referential constraint properties from Principal entities (possibly recursively)
        // and check referential constraint properties in the Dependent entities (1 level only)
        private void RetrieveAndCheckReferentialConstraintValues() 
        {
            RelationshipManager relationshipManager = EntityUtil.GetRelationshipManager(_entity); 
            if (relationshipManager != null) 
            {
                // Find key property names which are part of referential integrity constraints 
                List propertiesToRetrieve;  // names of properties which should be retrieved from Principal entities
                bool propertiesToCheckExist;        // true iff there are properties which should be checked in dependent entities

                // Get RI property names from metadata 
                relationshipManager.FindNamesOfReferentialConstraintProperties(out propertiesToRetrieve, out propertiesToCheckExist);
 
                // Do not try to retrieve RI properties if entity doesn't participate in any RI Constraints 
                if (propertiesToRetrieve != null)
                { 
                    // Retrieve key values from related entities
                    Dictionary> properties;

                    // Create HashSet to store references to already visited entities, used to detect circular references 
                    HashSet visited = new HashSet();
 
                    relationshipManager.RetrieveReferentialConstraintProperties(out properties, false /*includeOwnValues*/, visited); 

                    // Update properties 
                    foreach (KeyValuePair> pair in properties)
                    {
                        this.SetCurrentEntityValue(pair.Key /*name*/, pair.Value.Key /*value*/);
                    } 
                }
 
                if (propertiesToCheckExist) 
                {
                    // Compare properties of current entity with properties of the dependent entities 
                    this.CheckReferentialConstraintPropertiesInDependents();
                }
            }
        } 

 
        internal void RetrieveReferentialConstraintPropertiesFromKeyEntries(Dictionary> properties) 
        {
            string thisRole; 
            AssociationSet association;

            // Iterate through related relationship entries
            foreach (ObjectStateEntry relationshipEntry in _cache.FindRelationshipsByKey(EntityKey)) 
            {
                ObjectStateEntry otherEnd = this.GetOtherEndOfRelationship(relationshipEntry); 
 
                // We only try to retrieve properties from key entries
                if (otherEnd.IsKeyEntry) 
                {
                    association = (AssociationSet)relationshipEntry.EntitySet;
                    Debug.Assert(association != null, "relationship is not an association");
 
                    // Iterate through referential constraints of the association of the relationship
                    // NOTE PERFORMANCE This collection in current stack can have 0 or 1 elements 
                    foreach (ReferentialConstraint constraint in association.ElementType.ReferentialConstraints) 
                    {
                        thisRole = this.GetAssociationEndMember(relationshipEntry).Name; 

                        // Check if curent entry is a dependent end of the referential constraint
                        if (constraint.ToRole.Name == thisRole)
                        { 
                            Debug.Assert(!otherEnd.EntityKey.IsTemporary, "key of key entry can't be temporary");
                            IList otherEndKeyValues = otherEnd.EntityKey.EntityKeyValues; 
                            Debug.Assert(otherEndKeyValues != null, "key entry must have key values"); 

                            // NOTE PERFORMANCE Number of key properties is supposed to be "small" 
                            foreach (EntityKeyMember pair in otherEndKeyValues)
                            {
                                for (int i = 0; i < constraint.FromProperties.Count; ++i)
                                { 
                                    if (constraint.FromProperties[i].Name == pair.Key)
                                    { 
                                        ObjectStateEntry.AddOrIncreaseCounter(properties, constraint.ToProperties[i].Name, pair.Value); 
                                    }
                                } 
                            }
                        }
                    }
                } 
            }
        } 
 
        internal static void AddOrIncreaseCounter(Dictionary> properties, string propertyName, object propertyValue)
        { 
            Debug.Assert(properties != null);
            Debug.Assert(propertyName != null);
            Debug.Assert(propertyValue != null);
 
            if (properties.ContainsKey(propertyName))
            { 
                // If this property already exists in the dictionary, check if value is the same then increase the counter 

                KeyValuePair valueCounterPair = properties[propertyName]; 

                if (!valueCounterPair.Key.Equals(propertyValue))
                    throw EntityUtil.InconsistentReferentialConstraintProperties();
                else 
                    valueCounterPair.Value.Value = valueCounterPair.Value.Value + 1;
            } 
            else 
            {
                // If property doesn't exist in the dictionary - add new entry with pair 
                properties[propertyName] = new KeyValuePair(propertyValue, new IntBox(1));
            }
        }
 

        // Check if related dependent entities contain proper property values 
        // Only entities in Unchanged and Modified state are checked (including KeyEntries) 
        private void CheckReferentialConstraintPropertiesInDependents()
        { 
            string thisRole;
            AssociationSet association;

            // Iterate through related relationship entries 
            foreach (ObjectStateEntry relationshipEntry in _cache.FindRelationshipsByKey(EntityKey))
            { 
                ObjectStateEntry otherEnd = this.GetOtherEndOfRelationship(relationshipEntry); 

                // We only check entries which are in Unchanged or Modified state 
                // (including KeyEntries which are always in Unchanged State)
                if (otherEnd.State == EntityState.Unchanged || otherEnd.State == EntityState.Modified)
                {
                    association = (AssociationSet)relationshipEntry.EntitySet; 
                    Debug.Assert(association != null, "relationship is not an association");
 
                    // Iterate through referential constraints of the association of the relationship 
                    // NOTE PERFORMANCE This collection in current stack can have 0 or 1 elements
                    foreach (ReferentialConstraint constraint in association.ElementType.ReferentialConstraints) 
                    {
                        thisRole = this.GetAssociationEndMember(relationshipEntry).Name;

                        // Check if curent entry is a principal end of the referential constraint 
                        if (constraint.FromRole.Name == thisRole)
                        { 
                            Debug.Assert(!otherEnd.EntityKey.IsTemporary, "key of Unchanged or Modified entry can't be temporary"); 
                            IList otherEndKeyValues = otherEnd.EntityKey.EntityKeyValues;
                            // NOTE PERFORMANCE Number of key properties is supposed to be "small" 
                            foreach (EntityKeyMember pair in otherEndKeyValues)
                            {
                                for (int i = 0; i < constraint.FromProperties.Count; ++i)
                                { 
                                    if (constraint.ToProperties[i].Name == pair.Key)
                                    { 
                                        if (!this.GetCurrentEntityValue(constraint.FromProperties[i].Name).Equals(pair.Value)) 
                                        {
                                            throw EntityUtil.InconsistentReferentialConstraintProperties(); 
                                        }
                                    }
                                }
                            } 
                        }
                    } 
                } 
            }
        } 

        /// 
        /// API to mark the entity deleted. if entity is in added state, it will be detached
        ///  
        /// 
        ///   
        public void Delete() 
        {
            // doFixup flag is used for Cache and Collection & Ref consistency 
            // When some entity is deleted if "doFixup" is true then Delete method
            // calls the Collection & Ref code to do the necessary fix-ups.
            // "doFixup" equals to False is only called from EntityCollection & Ref code
            Delete(/*doFixup*/true); 
        }
 
        //"doFixup" equals to False is called from EntityCollection & Ref code only 
        internal void Delete(bool doFixup)
        { 
            ValidateState();

            if (IsKeyEntry)
            { 
                throw EntityUtil.CannotCallDeleteOnKeyEntry();
            } 
 
            if (IsRelationship && doFixup)
            { 
                if (State != EntityState.Deleted)  //for deleted ObjectStateEntry its a no-op
                {
                    //Find two ends of the relationship
                    ObjectStateEntry entry1 = _cache.GetObjectStateEntry((EntityKey)GetCurrentRelationValue(0)); 
                    IEntityWithRelationships entity1 = entry1.Entity as IEntityWithRelationships;
                    ObjectStateEntry entry2 = _cache.GetObjectStateEntry((EntityKey)GetCurrentRelationValue(1)); 
                    IEntityWithRelationships entity2 = entry2.Entity as IEntityWithRelationships; 

                    // If one end of the relationship is a KeyEntry, entity1 or entity2 is null. 
                    // It is not possible that both ends of relationship are KeyEntries.
                    if (entity1 != null && entity2 != null)
                    {
                        //Create navigation 
                        ReadOnlyMetadataCollection endMembers = _wrapper.AssociationEndMembers;
                        string role1 = endMembers[0].Name; 
                        string role2 = endMembers[1].Name; 
                        string relationshipName = ((AssociationSet)_entitySet).ElementType.FullName;
                        RelationshipNavigation navigation = new RelationshipNavigation(relationshipName, role1, role2); 

                        entity1.RelationshipManager.RemoveEntity(navigation, entity2);
                    }
                    else 
                    {
                        // One end of relationship is a KeyEntry, figure out which one is the real entity and get its RelationshipManager 
                        // so we can update the DetachedEntityKey on the EntityReference associated with this relationship 
                        EntityKey targetKey = null;
                        RelationshipManager relationshipManager = null; 
                        if (entity1 == null)
                        {
                            targetKey = entry1.EntityKey;
                            relationshipManager = EntityUtil.GetRelationshipManager(entity2); 
                        }
                        else 
                        { 
                            targetKey = entry2.EntityKey;
                            relationshipManager = EntityUtil.GetRelationshipManager(entity1); 
                        }

                        // Clear the detachedEntityKey as well. In cases where we have to fix up the detachedEntityKey, we will not always be able to detect
                        // if we have *only* a Deleted relationship for a given entity/relationship/role, so clearing this here will ensure that 
                        // even if no other relationships are added, the key value will still be correct and we won't accidentally pick up an old value.
 
                        // devnote: Since we know the target end of this relationship is a key entry, it has to be a reference, so just cast 
                        AssociationEndMember targetMember = this.Wrapper.GetAssociationEndMember(targetKey);
                        EntityReference entityReference = (EntityReference)relationshipManager.GetRelatedEnd(targetMember.DeclaringType.FullName, targetMember.Name); 
                        entityReference.DetachedEntityKey = null;

                        // Now update the state
                        if (this.State == EntityState.Added) 
                        {
                            // Remove key entry if necessary 
                            DeleteUnnecessaryKeyEntries(); 
                            // Remove relationship entry
                            // devnote: Using this method instead of just changing the state because the entry 
                            //          may have already been detached along with the key entry above. However,
                            //          if there were other relationships using the key, it would not have been deleted.
                            DetachRelationshipEntry();
                        } 
                        else
                        { 
                            // Non-added entries should be deleted 
                            _cache.ChangeState(this, this.State, EntityState.Deleted);
                            State = EntityState.Deleted; 
                        }
                    }
                }
            } 
            else
            { 
                switch (State) 
                {
                    case EntityState.Added: 
                        if (doFixup)
                        {
                            FixupRelationships();
                        } 
                        if (State != EntityState.Detached) //There is a possibility that as part of FixupRelationship entity moved to Detached state.
                        { 
                            Debug.Assert(EntityState.Added == State, "Expected ObjectStateEntry state is Added; make sure FixupRelationship did not corrupt cache entry state"); 
                            if (IsRelationship)
                            { 
                                // Remove key entry if necessary
                                DeleteUnnecessaryKeyEntries();
                                // Remove relationship entry
                                // devnote: Using this method instead of just changing the state because the entry 
                                //          may have already been detached along with the key entry above. However,
                                //          if there were other relationships using the key, it would not have been deleted. 
                                DetachRelationshipEntry(); 
                            }
                            else 
                            {
                                _cache.ChangeState(this, EntityState.Added, EntityState.Detached);
                            }
                            Debug.Assert(null == _modifiedFields, "There should not be any modified fields"); 
                        }
                        break; 
                    case EntityState.Modified: 
                        if (doFixup)
                        { 
                            FixupRelationships();
                        }
                        if (State != EntityState.Deleted) //There is a possibility that as part of FixupRelationship entity moved to Deleted state.
                        { 
                            Debug.Assert(EntityState.Modified == State, "Expected ObjectStateEntry state is Modified; make sure FixupRelationship did not corrupt cache entry state");
                            _cache.ChangeState(this, EntityState.Modified, EntityState.Deleted); 
                            State = EntityState.Deleted; 
                        }
                        break; 
                    case EntityState.Unchanged:
                        if (doFixup)
                        {
                            FixupRelationships(); 
                        }
                        else if (!IsRelationship) 
                        { 
                            // Even when we are not doing relationship fixup at the collection level, if the entry is not a relationship
                            // we need to check to see if there are relationships that are referencing keys that should be removed 
                            // this mainly occurs in cascade delete scenarios
                            DeleteRelationshipsThatReferenceKeys(null, null);
                        }
                        if (State != EntityState.Deleted)  //There is a possibility that as part of FixupRelationship entity moved to Deleted state. 
                        {
                            Debug.Assert(EntityState.Unchanged == State, "Expected ObjectStateEntry state is Unchanged; make sure FixupRelationship did not corrupt cache entry state"); 
                            _cache.ChangeState(this, EntityState.Unchanged, EntityState.Deleted); 
                            Debug.Assert(null == _modifiedFields, "There should not be any modified fields");
                            State = EntityState.Deleted; 
                        }
                        break;
                    //case DataRowState.Deleted:  no-op
                } 
            }
        } 
 
        private void FixupRelationships()
        { 
            RelationshipManager relationshipManager = EntityUtil.GetRelationshipManager(_entity);
            if (relationshipManager != null)
            {
                relationshipManager.RemoveEntityFromRelationships(); 
                DeleteRelationshipsThatReferenceKeys(null, null);
            } 
        } 

        ///  
        /// see if there are any relationship entries that point to key entries
        /// if there are, remove the relationship entry
        /// This is called when one of the ends of a relationship is being removed
        ///  
        /// An option relationshipSet; deletes only relationships that are part of this set
        internal void DeleteRelationshipsThatReferenceKeys(RelationshipSet relationshipSet, RelationshipEndMember endMember) 
        { 
            if (State != EntityState.Detached)
            { 
                // devnote: Need to use a copy of the relationships list because we may be deleting Added
                //          relationships, which will be removed from the list while we are still iterating
                foreach (ObjectStateEntry relationshipEntry in _cache.CopyOfRelationshipsByKey(EntityKey))
                { 
                    // Only delete the relationship entry if it is not already deleted (in which case we cannot access its values)
                    // and when the given (optionally) relationshipSet matches the one in teh relationship entry 
                    if ((relationshipEntry.State != EntityState.Deleted) && 
                        (relationshipSet == null || relationshipSet == relationshipEntry.EntitySet))
                    { 
                        ObjectStateEntry otherEnd = this.GetOtherEndOfRelationship(relationshipEntry);
                        if (endMember == null || endMember == otherEnd.GetAssociationEndMember(relationshipEntry))
                        {
                            for (int i = 0; i < 2; i++) 
                            {
                                EntityKey entityKey = relationshipEntry.GetCurrentRelationValue(i, false) as EntityKey; 
                                if ((object)entityKey != null) 
                                {
                                    ObjectStateEntry relatedEntry = _cache.GetObjectStateEntry(entityKey); 
                                    if (relatedEntry.IsKeyEntry)
                                    {
                                        // remove the relationshipEntry
                                        relationshipEntry.Delete(false); 
                                        break;
                                    } 
                                } 
                            }
                        } 
                    }
                }
            }
        } 

        ///  
        /// Detaches an entry and create in its place key entry if necessary 
        /// Removes relationships with another key entries and removes these key entries if necessary
        ///  
        internal void Detach()
        {
            ValidateState();
 
            Debug.Assert(!IsRelationship);
            Debug.Assert(!IsKeyEntry); 
 
            bool createKeyEntry = false;
 
            RelationshipManager relationshipManager = EntityUtil.GetRelationshipManager(_entity);
            if (relationshipManager != null)
            {
                // Key entry should be created only when current entity is not in Added state 
                // and if the entity is a "OneToOne" or "ZeroToOne" end of some existing relationship.
                createKeyEntry = 
                        this.State != EntityState.Added && 
                        this.IsOneEndOfSomeRelationship();
 
                // Remove current entity from collections/references (on both ends of relationship)
                // Relationship entries are removed from ObjectStateManager if current entity is in Added state
                // or if current entity is a "Many" end of the relationship.
                // NOTE In this step only relationship entries which have normal entity on the other end 
                //      can be removed.
                relationshipManager.DetachEntityFromRelationships(this.State); 
 
                // Remove relationship entries which has a key entry on the other end.
                // If the key entry does not have any other relationship, it is removed from Object State Manager. 
                // NOTE Relationship entries which have a normal entity on the other end are not modified in this step -
                //      they were modified in the previous step.
                this.DetachRelationshipsEntries(relationshipManager);
 
                if (createKeyEntry)
                { 
                    this.DegradeEntry(); 
                }
            } 

            if (!createKeyEntry)
            {
                // If entity is in state different than Added state, entityKey should not be set to null 
                // EntityKey is set to null in
                //    ObjectStateManger.ChangeState() -> 
                //    ObjectStateEntry.Reset() -> 
                //    ObjectStateEntry.DetachObjectStateManagerFromEntity()
 
                // Store data required to restore the entity key if needed.
                EntityKey key = _entityKey;
                EntityState state = State;
                object entity = _entity; 

                _cache.ChangeState(this, this.State, EntityState.Detached); 
 
                // In case the detach event modifies the key.
                if (state != EntityState.Added) 
                {
                    EntityUtil.SetKeyOntoEntity(entity, key);
                }
            } 
        }
 
        private bool IsOneEndOfSomeRelationship() 
        {
            foreach (ObjectStateEntry relationshipEntry in _cache.FindRelationshipsByKey(EntityKey)) 
            {
                RelationshipMultiplicity multiplicity = this.GetAssociationEndMember(relationshipEntry).RelationshipMultiplicity;
                if (multiplicity == RelationshipMultiplicity.One ||
                    multiplicity == RelationshipMultiplicity.ZeroOrOne) 
                {
                    return true; 
                } 
            }
            return false; 
        }

        internal void DetachRelationshipEntry()
        { 
            // no-op if already detached
            if (_cache != null) 
            { 
                Debug.Assert(IsRelationship, "this should be relationship entry");
                _cache.ChangeState(this, this.State, EntityState.Detached); 
            }
        }

        internal void RevertDelete() 
        {            // just change the state from deleted, to last state.
            if (!IsRelationship) 
            { 
                State = (_modifiedFields == null) ? EntityState.Unchanged : EntityState.Modified;
            } 
            else
            {
                State = EntityState.Unchanged;
            } 
            _cache.ChangeState(this, EntityState.Deleted, State);
        } 
 
        internal void Reset()
        { 
            DetachObjectStateManagerFromEntity();
            _cache = null;
            _entity = null;
            _entityKey = null; 
            _entitySet = null;
            _modifiedFields = null; 
            _originalValues = null; 
            _wrapper = null;
            State = EntityState.Detached; 
        }

        private static void ValidateRelationshipRange(int ordinal)
        { 
            if (unchecked(1u < (uint)ordinal))
            { 
                throw EntityUtil.ArgumentOutOfRange("ordinal"); 
            }
        } 

        private void ValidateState()
        {
            if (State == EntityState.Detached) 
            {
                throw EntityUtil.ObjectStateEntryinInvalidState(); 
            } 
            Debug.Assert(null != _cache, "null ObjectStateManager");
            Debug.Assert(null != _entitySet, "null EntitySetBase"); 
        }

        /// 
        /// API to return properties that are marked modified 
        /// 
        ///   
        ///  IEnumerable of modified properties names, names are in term of c-space  
        public IEnumerable GetModifiedProperties()
        { 
            ValidateState();
            if (EntityState.Modified == _state && _modifiedFields != null)
            {
                Debug.Assert(null != _modifiedFields, "null fields"); 
                for (int i = 0; i < _modifiedFields.Count; i++)
                { 
                    if (_modifiedFields[i]) 
                    {
                        yield return (IsRelationship ? GetCLayerName(i, null) : GetCLayerName(i, _cacheTypeMetadata)); 
                    }
                }
            }
        } 

        #region IEntityChangeTracker 
 
        /// 
        /// Used to report that a scalar entity property is about to change 
        /// The current value of the specified property is cached when this method is called.
        /// 
        /// The name of the entity property that is changing
        void IEntityChangeTracker.EntityMemberChanging(string entityMemberName) 
        {
            EntityValueChanging(entityMemberName, null, null); 
        } 

        ///  
        /// Used to report that a scalar entity property has been changed
        /// The property value that was cached during EntityMemberChanging is now
        /// added to OriginalValues
        ///  
        /// The name of the entity property that has changing
        void IEntityChangeTracker.EntityMemberChanged(string entityMemberName) 
        { 
            EntityValueChanged(entityMemberName, null, null);
        } 

        /// 
        /// Used to report that a complex property is about to change
        /// The current value of the specified property is cached when this method is called. 
        /// 
        /// The name of the top-level entity property that is changing 
        /// The complex object that contains the property that is changing 
        /// The name of the property that is changing on complexObject
        void IEntityChangeTracker.EntityComplexMemberChanging(string entityMemberName, object complexObject, string complexObjectMemberName) 
        {
            EntityUtil.CheckArgumentNull(complexObjectMemberName, "complexObjectMemberName");
            EntityUtil.CheckArgumentNull(complexObject, "complexObject");
            EntityValueChanging(entityMemberName, complexObject, complexObjectMemberName); 
        }
 
        ///  
        /// Used to report that a complex property has been changed
        /// The property value that was cached during EntityMemberChanging is now added to OriginalValues 
        /// 
        /// The name of the top-level entity property that has changed
        /// The complex object that contains the property that changed
        /// The name of the property that changed on complexObject 
        void IEntityChangeTracker.EntityComplexMemberChanged(string entityMemberName, object complexObject, string complexObjectMemberName)
        { 
            EntityUtil.CheckArgumentNull(complexObjectMemberName, "complexObjectMemberName"); 
            EntityUtil.CheckArgumentNull(complexObject, "complexObject");
            EntityValueChanged(entityMemberName, complexObject, complexObjectMemberName); 
        }

        /// 
        /// Returns the EntityState from the ObjectStateEntry 
        /// 
        EntityState IEntityChangeTracker.EntityState 
        { 
            get
            { 
                return this.State;
            }
        }
 
        #endregion
 
        #region Private Change Tracking Methods 

        ///  
        /// Method called to start the change tracking process on an entity property. The current property value is cached at
        /// this stage in preparation for later storage in the original values record. Multiple successful calls to this method
        /// will overwrite the cached values.
        /// All inputs are in OSpace. 
        /// 
        /// Name of the top-level entity property that is changing 
        /// If entityMemberName refers to a complex property, this is the complex 
        /// object that contains the change. Otherwise this is null.
        /// If entityMemberName refers to a complex property, this is the name of 
        /// the property that is changing on complexObject. Otherwise this is null.
        private void EntityValueChanging(string entityMemberName, object complexObject, string complexObjectMemberName)
        {
            string changingMemberName; 
            StateManagerTypeMetadata typeMetadata;
            object changingObject; 
 
            // Get the metadata for the property that is changing, and verify that it is valid to change it for this entry
            int changingOrdinal = GetAndValidateChangeMemberInfo(entityMemberName, complexObject, complexObjectMemberName, 
                out typeMetadata, out changingMemberName, out changingObject);

            // if EntityKey is changing and is in a valid scenario for it to change, no further action is needed
            if (changingOrdinal == -2) 
            {
                return; 
            } 

            Debug.Assert(changingOrdinal != -1, "Expected GetAndValidateChangeMemberInfo to throw for a invalid property name"); 

            // Cache the current value for later storage in original values. If we are not in a state where we should update
            // the original values, we don't even need to bother saving the current value here. However, we will still cache
            // the other data regarding the change, so that we always require matching Changing and Changed calls, regardless of the state. 
            StateManagerMemberMetadata memberMetadata = typeMetadata.Member(changingOrdinal);
            object oldValue = null; 
            if (_state == EntityState.Unchanged || 
                (_state == EntityState.Modified && !FindOriginalValue(memberMetadata, changingObject)))
            { 
                // devnote: Not using GetCurrentEntityValue here because change tracking can only be done on OSpace members,
                //          so we don't need to worry about shadow state, and we don't want a CSpace representation of complex objects

                oldValue = memberMetadata.GetValue(changingObject); 
                _cache.SaveOriginalValues = true;
            } 
            else 
            {
                // we are not in a state where the values will be saved 
                _cache.SaveOriginalValues = false;
            }
            Debug.Assert(this.State != EntityState.Detached, "Change tracking should not happen on detached entities.");
            SetCachedChangingValues(entityMemberName, changingObject, changingMemberName, this.State, oldValue); 
        }
 
        ///  
        /// Method called to complete the change tracking process on an entity property. The original property value
        /// is now saved in the original values record if there is not already an entry in the record for this property. 
        /// The parameters to this method must have the same values as the parameter values passed to the last call to
        /// EntityValueChanging on this ObjectStateEntry.
        /// All inputs are in OSpace.
        ///  
        /// Name of the top-level entity property that has changed
        /// If entityMemberName refers to a complex property, this is the complex 
        /// object that contains the change. Otherwise this is null. 
        /// If entityMemberName refers to a complex property, this is the name of
        /// the property that has changed on complexObject. Otherwise this is null. 
        private void EntityValueChanged(string entityMemberName, object complexObject, string complexObjectMemberName)
        {
            string changingMemberName;
            StateManagerTypeMetadata typeMetadata; 
            object changingObject;
 
            // Get the metadata for the property that is changing, and verify that it is valid to change it for this entry 
            // If something fails, we will clear out our cached values in the finally block, and require the user to submit another Changing notification
            try 
            {
                int changingOrdinal = GetAndValidateChangeMemberInfo(entityMemberName, complexObject, complexObjectMemberName,
                    out typeMetadata, out changingMemberName, out changingObject);
 
                // if EntityKey is changing and is in a valid scenario for it to change, no further action is needed
                if (changingOrdinal == -2) 
                { 
                    return;
                } 

                // Verify that the inputs to this call match the values we have cached
                if ((changingObject != _cache.ChangingObject) ||
                    (changingMemberName != _cache.ChangingMember) || 
                    (entityMemberName != _cache.ChangingEntityMember))
                { 
                    throw EntityUtil.EntityValueChangedWithoutEntityValueChanging(); 
                }
 
                // check the state after the other values because if the other cached values have not been set and are null, it is more
                // intuitive to the user to get an error that specifically points to that as the problem, and in that case, the state will
                // also not be matched, so if we checked this first, it would cause a confusing error to be thrown.
                if (this.State != _cache.ChangingState) 
                {
                    throw EntityUtil.ChangedInDifferentStateFromChanging(this.State, _cache.ChangingState); 
                } 

                if (_cache.SaveOriginalValues) 
                {
                    StateManagerMemberMetadata memberMetadata = typeMetadata.Member(changingOrdinal);
                    object oldValue = _cache.ChangingOldValue;
 
                    // Expand only non-null complex type values
                    if (memberMetadata.IsComplex && oldValue != null) 
                    { 
                        // devnote: Not using GetCurrentEntityValue here because change tracking can only be done on OSpace members,
                        //          so we don't need to worry about shadow state, and we don't want a CSpace representation of complex objects 
                        object newValue = memberMetadata.GetValue(changingObject);
                        ExpandComplexTypeAndAddValues(memberMetadata, oldValue, newValue, false);
                    }
                    else 
                    {
                        AddOriginalValue(memberMetadata, changingObject, oldValue); 
                    } 
                }
 
                EntityState initialState = State;
                if (State != EntityState.Added)
                {
                    State = EntityState.Modified; 
                }
                if (State == EntityState.Modified) 
                { 
                    SetModifiedProperty(entityMemberName);
                } 
                if (initialState != _state)
                {
                    _cache.ChangeState(this, initialState, _state);
                } 
            }
            finally 
            { 
                SetCachedChangingValues(null, null, null, EntityState.Detached, null);
            } 
        }


 
        /// 
        /// Helper method to validate that the property names being reported as changing/changed are valid for this entity and that 
        /// the entity is in a valid state for the change request. Also determines if this is a change on a complex object, and 
        /// returns the appropriate metadata and object to be used for the rest of the changing and changed operations.
        ///  
        /// Top-level entity property name
        /// Complex object that contains the change, null if the change is on a top-level entity property
        /// Name of the property that is changing on the complexObject, null for top-level entity properties
        /// Metadata for the type that contains the change, either for the entity itself or for the complex object 
        /// Property name that is actually changing -- either entityMemberName for entities or
        /// complexObjectMemberName for complex objects 
        /// Object reference that contains the change, either the entity or complex object 
        /// as appropriate for the requested change
        /// Ordinal of the property that is changing, or -2 if the EntityKey is changing in a valid scenario. This is relative 
        /// to the returned typeMetadata. Throws exceptions if the requested property name(s) are invalid for this entity.
        private int GetAndValidateChangeMemberInfo(string entityMemberName, object complexObject, string complexObjectMemberName,
            out StateManagerTypeMetadata typeMetadata, out string changingMemberName, out object changingObject)
        { 
            typeMetadata = null;
            changingMemberName = null; 
            changingObject = null; 

            Debug.Assert(!IsRelationship, "Change tracking methods should not be called on relationship entries."); 

            EntityUtil.CheckArgumentNull(entityMemberName, "entityMemberName");
            // complexObject and complexObjectMemberName are allowed to be null here for change tracking on top-level entity properties
 
            ValidateState();
 
            int changingOrdinal = _cacheTypeMetadata.GetOrdinalforOLayerMemberName(entityMemberName); 
            if (changingOrdinal == -1)
            { 
                if (entityMemberName == StructuralObject.EntityKeyPropertyName)
                {
                    // Setting EntityKey property is only allowed from here when we are in the middle of relationship fixup.
                    if (!_cache.InRelationshipFixup) 
                    {
                        throw EntityUtil.CantSetEntityKey(); 
                    } 
                    else
                    { 
                        // If we are in fixup, there is nothing more to do here with EntityKey, so just
                        // clear the saved changing values and return. This will ensure that we behave
                        // the same with the change notifications on EntityKey as with other properties.
                        // I.e. we still don't allow the following: 
                        //     EntityMemberChanging("Property1")
                        //     EntityMemberChanging("EntityKey") 
                        //     EntityMemberChanged("EntityKey") 
                        //     EntityMemberChanged("Property1")
                        Debug.Assert(this.State != EntityState.Detached, "Change tracking should not happen on detached entities."); 
                        SetCachedChangingValues(null, null, null, this.State, null);
                        return -2;
                    }
                } 
                else
                { 
                    throw EntityUtil.ChangeOnUnmappedProperty(entityMemberName); 
                }
            } 
            else
            {
                StateManagerTypeMetadata tmpTypeMetadata;
                string tmpChangingMemberName; 
                object tmpChangingObject;
 
                // entityMemberName is a confirmed valid property on the Entity, but if this is a complex type we also need to validate its property 
                if (complexObject != null)
                { 
                    // a complex object was provided, but the top-level Entity property is not complex
                    if (!_cacheTypeMetadata.Member(changingOrdinal).IsComplex)
                    {
                        throw EntityUtil.ComplexChangeRequestedOnScalarProperty(entityMemberName); 
                    }
 
                    tmpTypeMetadata = _cache.GetOrAddStateManagerTypeMetadata(complexObject.GetType(), (EntitySet)this.EntitySet); 
                    changingOrdinal = tmpTypeMetadata.GetOrdinalforOLayerMemberName(complexObjectMemberName);
                    if (changingOrdinal == -1) 
                    {
                        throw EntityUtil.ChangeOnUnmappedComplexProperty(complexObjectMemberName);
                    }
 
                    tmpChangingMemberName = complexObjectMemberName;
                    tmpChangingObject = complexObject; 
                } 
                else
                { 
                    tmpTypeMetadata = _cacheTypeMetadata;
                    tmpChangingMemberName = entityMemberName;
                    tmpChangingObject = this.Entity;
                } 

                VerifyEntityValueIsEditable(tmpTypeMetadata, changingOrdinal, tmpChangingMemberName); 
 
                typeMetadata = tmpTypeMetadata;
                changingMemberName = tmpChangingMemberName; 
                changingObject = tmpChangingObject;
                return changingOrdinal;
            }
        } 

        ///  
        /// Helper method to set the information needed for the change tracking cache. Ensures that all of these values get set together. 
        /// 
        private void SetCachedChangingValues(string entityMemberName, object changingObject, string changingMember, EntityState changingState, object oldValue) 
        {
            _cache.ChangingEntityMember = entityMemberName;
            _cache.ChangingObject = changingObject;
            _cache.ChangingMember = changingMember; 
            _cache.ChangingState = changingState;
            _cache.ChangingOldValue = oldValue; 
            if (changingState == EntityState.Detached) 
            {
                _cache.SaveOriginalValues = false; 
            }
        }

        ///  
        /// Helper method to recursively expand a complex object's values down to scalars for storage in the original values record.
        /// This method is used when a whole complex object is set on its parent object, instead of just setting 
        /// individual scalar values on that object. 
        /// 
        /// metadata for the complex property being expanded on the parent 
        /// where the parent can be an entity or another complex object
        /// Old value of the complex property. Scalar values from this object are stored in the original values record
        /// New value of the complex property. This object reference is used in the original value record and is
        /// associated with the scalar values for the same property on the oldComplexObject 
        /// Whether or not to use the existing complex object in the original values or to use the original value that is already present 
        private void ExpandComplexTypeAndAddValues(StateManagerMemberMetadata memberMetadata, object oldComplexObject, object newComplexObject, bool useOldComplexObject) 
        { 
            Debug.Assert(newComplexObject != null, "null new object(s) cannot be used in complex object expansion");
            Debug.Assert(memberMetadata.IsComplex, "Cannot expand non-complex objects"); 
            Debug.Assert(oldComplexObject == null || (oldComplexObject.GetType() == newComplexObject.GetType()), "Cannot replace a complex object with an object of a different type, unless the original one was null");

            StateManagerTypeMetadata typeMetadata = _cache.GetOrAddStateManagerTypeMetadata(memberMetadata.CdmMetadata.TypeUsage.EdmType);
            object retValue; 
            for (int field = 0; field < typeMetadata.FieldCount; field++)
            { 
                StateManagerMemberMetadata complexMemberMetadata = typeMetadata.Member(field); 
                if (complexMemberMetadata.IsComplex)
                { 
                    object oldComplexMemberValue = null;
                    if (oldComplexObject != null)
                    {
                        oldComplexMemberValue = complexMemberMetadata.GetValue(oldComplexObject); 
                        if (oldComplexMemberValue == null && FindOriginalValue(complexMemberMetadata, oldComplexObject, out retValue))
                        { 
                            _originalValues.Remove((StateManagerValue)retValue); 
                        }
                    } 
                    ExpandComplexTypeAndAddValues(complexMemberMetadata, oldComplexMemberValue, complexMemberMetadata.GetValue(newComplexObject), useOldComplexObject);
                }
                else
                { 
                    object originalValue = null;
                    object complexObject = newComplexObject; 
 
                    if (useOldComplexObject)
                    { 
                        // Set the original values using the existing current value object
                        // complexObject --> the existing complex object
                        // originalValue --> the new value to set for this member
                        originalValue = complexMemberMetadata.GetValue(newComplexObject); 
                        complexObject = oldComplexObject;
                    } 
                    else 
                    {
                        if (oldComplexObject != null) 
                        {
                            // If we already have an entry for this property in the original values list, we need to remove it. We can't just
                            // update it because StateManagerValue is a struct and there is no way to get a reference to the entry in the list.
                            originalValue = complexMemberMetadata.GetValue(oldComplexObject); 
                            if (FindOriginalValue(complexMemberMetadata, oldComplexObject, out retValue))
                            { 
                                StateManagerValue originalStateValue = ((StateManagerValue)retValue); 
                                _originalValues.Remove(originalStateValue);
                                originalValue = originalStateValue.originalValue; 
                            }
                        }
                        else
                        { 
                            originalValue = complexMemberMetadata.GetValue(newComplexObject);
                        } 
                    } 

 
                    // Add the new entry. The userObject will reference the new complex object that is currently being set.
                    // If the value was in the list previously, we will still use the old value with the new object reference.
                    // That will ensure that we preserve the old value while still maintaining the link to the
                    // existing complex object that is attached to the entity or parent complex object. If an entry is already 
                    // in the list this means that it was either explicitly set by the user or the entire complex type was previously
                    // set and expanded down to the individual properties.  In either case we do the same thing. 
                    AddOriginalValue(complexMemberMetadata, complexObject, originalValue); 
                }
            } 
        }

        #endregion
 
        /// 
        /// Verifies that the property with the given ordinal is editable. 
        ///  
        /// the property is not editable
        internal void VerifyEntityValueIsEditable(StateManagerTypeMetadata typeMetadata, int ordinal, string memberName) 
        {
            if (_state == EntityState.Deleted)
            {
                throw EntityUtil.CantModifyDetachedDeletedEntries(); 
            }
 
            Debug.Assert(typeMetadata != null, "Cannot verify entity or complex object is editable if typeMetadata is null."); 
            StateManagerMemberMetadata member = typeMetadata.Member(ordinal);
 
            Debug.Assert(member != null, "Member shouldn't be null.");

            // Key fields are only editable if the entry is the Added state.
            if (member.IsPartOfKey && State != EntityState.Added) 
            {
                throw EntityUtil.CannotModifyKeyProperty(memberName); 
            } 
        }
 
        /// 
        /// set the state to Modified.
        /// 
        ///  
        /// 
        /// If State is not Modified or Unchanged 
        /// 
        public void SetModified()
        { 
            ValidateState();

            if (IsRelationship)
            { 
                throw EntityUtil.CantModifyRelationState();
            } 
            else if (IsKeyEntry) 
            {
                throw EntityUtil.CannotModifyKeyEntryState(); 
            }
            else
            {
                if (EntityState.Unchanged == State) 
                {
                    State = EntityState.Modified; 
                    _cache.ChangeState(this, EntityState.Unchanged, State); 
                }
                else if (EntityState.Modified != State) 
                {
                    throw EntityUtil.SetModifiedStates();
                }
            } 
        }
 
        ///  
        /// Marks specified property as modified.
        ///  
        /// This API recognizes the names in terms of OSpace
        /// If State is not Modified or Unchanged
        ///
        public void SetModifiedProperty(string propertyName) 
        {
            ValidateState(); 
            if (IsRelationship) 
            {
                throw EntityUtil.CantModifyRelationState(); 
            }
            else if (IsKeyEntry)
            {
                throw EntityUtil.CannotModifyKeyEntryState(); 
            }
            else 
            { 
                if (EntityState.Unchanged == State)
                { 
                    State = EntityState.Modified;
                    _cache.ChangeState(this, EntityState.Unchanged, State);
                }
                if (EntityState.Modified == State) 
                {
                    if (null == _modifiedFields) 
                    { 
                        _modifiedFields = new BitArray(GetFieldCount(_cacheTypeMetadata));
                    } 

                    EntityUtil.CheckArgumentNull(propertyName, "propertyName");
                    int ordinal = _cacheTypeMetadata.GetOrdinalforOLayerMemberName(propertyName);
                    if (ordinal == -1) 
                    {
                        throw EntityUtil.InvalidModifiedPropertyName(propertyName); 
                    } 
                    _modifiedFields.Set(ordinal, true);
                } 
                else
                {
                    throw EntityUtil.SetModifiedStates();
                } 
            }
        } 
 
        internal void SetModifiedAll()
        { 
            ValidateState();
            if (!IsRelationship)
            {
                Debug.Assert(State == EntityState.Modified, "SetModifiedAll called when not modified"); 
                if (null == _modifiedFields)
                { 
                    _modifiedFields = new BitArray(GetFieldCount(_cacheTypeMetadata)); 
                }
                _modifiedFields.SetAll(true); 
            }
            else
            {
                throw EntityUtil.CantModifyRelationState(); 
            }
        } 
 
        /// 
        /// Reuse or create a new (Entity)DataRecordInfo. 
        /// 
        internal DataRecordInfo GetDataRecordInfo(StateManagerTypeMetadata metadata, object userObject)
        {
            if (IsRelationship) 
            {
                //Dev Note: RelationshipType always has default facets. Thus its safe to construct a TypeUsage from EdmType 
                return new DataRecordInfo(TypeUsage.Create(((RelationshipSet)EntitySet).ElementType)); 
            }
            else if (Helper.IsEntityType(metadata.CdmMetadata.EdmType) && (null != (object)_entityKey)) 
            {
                // is EntityType with null EntityKey when constructing new EntityKey during ObjectStateManager.Add
                // always need a new EntityRecordInfo instance for the different key (reusing DataRecordInfo's FieldMetadata).
                return new EntityRecordInfo(metadata.DataRecordInfo, _entityKey, (EntitySet)EntitySet); 
            }
            else 
            { 
                // ObjectContext.AttachTo uses CurrentValueRecord to build EntityKey for EntityType
                // so the Entity doesn't have an EntityKey yet, SQLBU 525130 
                //Debug.Assert(Helper.IsComplexType(metadata.CdmMetadata.EdmType), "!IsComplexType");
                return metadata.DataRecordInfo;
            }
        } 

        // This API are mainly for DbDataRecord implementations to get and set the values 
        // also for loadoptions, setoldvalue will be used. 
        // we should handle just for C-space, we will not recieve a call from O-space for set
        // We will not also return any value in term of O-Layer. all set and gets for us is in terms of C-layer. 
        // the only O-layer interaction we have is through delegates from entity.
        internal void SetCurrentEntityValue(StateManagerTypeMetadata metadata, int ordinal, object userObject, object newValue)
        {
            // required to validate state because entity could be detatched from this context and added to another context 
            // and we want this to fail instead of setting the value which would redirect to the other context
            ValidateState(); 
 
            StateManagerMemberMetadata member = metadata.Member(ordinal);
            Debug.Assert(member != null, "StateManagerMemberMetadata was not found for the given ordinal."); 

            if (member.IsComplex)
            {
                if (newValue == null) 
                {
                    throw EntityUtil.NullableComplexTypesNotSupported(member.CLayerName); 
                } 

                IExtendedDataRecord newValueRecord = newValue as IExtendedDataRecord; 
                if (newValueRecord == null)
                {
                    throw EntityUtil.InvalidComplexDataRecordObject(member.CLayerName);
                } 

                newValue = _cache.ComplexTypeMaterializer.CreateComplex(newValueRecord, newValueRecord.DataRecordInfo, null); 
            } 

            member.SetValue(userObject, newValue); 
        }

        // helper method used to set value of property
        internal void SetCurrentEntityValue(string memberName, object newValue) 
        {
            int ordinal = _cacheTypeMetadata.GetOrdinalforOLayerMemberName(memberName); 
            SetCurrentEntityValue(_cacheTypeMetadata, ordinal, _entity, newValue); 
        }
 
        internal void SetOriginalEntityValue(StateManagerTypeMetadata metadata, int ordinal, object userObject, object newValue)
        {
            ValidateState();
            if (State == EntityState.Added) 
            {
                throw EntityUtil.OriginalValuesDoesNotExist(); 
            } 

            EntityState initialState = State; 

            object orgValue; // StateManagerValue
            object oldOriginalValue; // the actual value
 
            // Update original values list
            StateManagerMemberMetadata memberMetadata = metadata.Member(ordinal); 
            if (FindOriginalValue(memberMetadata, userObject, out orgValue)) 
            {
                _originalValues.Remove((StateManagerValue)orgValue); 
            }

            if (memberMetadata.IsComplex)
            { 
                oldOriginalValue = memberMetadata.GetValue(userObject);
                if (oldOriginalValue == null) 
                { 
                    throw EntityUtil.NullableComplexTypesNotSupported(memberMetadata.CLayerName);
                } 

                IExtendedDataRecord newValueRecord = newValue as IExtendedDataRecord;
                if (newValueRecord != null)
                { 
                    // Requires materialization
                    newValue = _cache.ComplexTypeMaterializer.CreateComplex(newValueRecord, newValueRecord.DataRecordInfo, null); 
                } 

                // We only store scalar properties values in original values, so no need to search the list 
                // if the property being set is complex. Just get the value as an OSpace object.
                ExpandComplexTypeAndAddValues(memberMetadata, oldOriginalValue, newValue, true);
            }
            else 
            {
                AddOriginalValue(memberMetadata, userObject, newValue); 
            } 

            if (initialState == EntityState.Unchanged) 
            {
                State = EntityState.Modified;
            }
        } 

        private bool FindOriginalValue(StateManagerMemberMetadata metadata, object instance) 
        { 
            object tmp;
            return FindOriginalValue(metadata, instance, out tmp); 
        }
        private bool FindOriginalValue(StateManagerMemberMetadata metadata, object instance, out object value)
        {
            bool found = false; 
            object retValue = null;
            if (null != _originalValues) 
            { 
                foreach (StateManagerValue cachevalue in _originalValues)   // this should include also shadow state
                { 
                    if (cachevalue.userObject == instance && cachevalue.memberMetadata == metadata)
                    {
                        found = true;
                        retValue = cachevalue; 
                        break;
                    } 
                } 
            }
            value = retValue; 
            return found;
        }

        // Get AssociationEndMember of current entry of given relationship 
        // Relationship must be related to the current entry.
        internal AssociationEndMember GetAssociationEndMember(ObjectStateEntry relationshipEntry) 
        { 
            Debug.Assert(relationshipEntry.IsRelationship, "entry should be a relationship entry");
            Debug.Assert((object)this.EntityKey != null, "entry should have a not null EntityKey"); 

            ValidateState();

            AssociationEndMember endMember = relationshipEntry.Wrapper.GetAssociationEndMember(EntityKey); 
            Debug.Assert(null != endMember, "should be one of the ends of the relationship");
            return endMember; 
        } 

        // Get entry which is on the other end of given relationship. 
        // Relationship must be related to the current entry.
        internal ObjectStateEntry GetOtherEndOfRelationship(ObjectStateEntry relationshipEntry)
        {
            Debug.Assert(relationshipEntry.IsRelationship, "entry should be a relationship entry"); 
            Debug.Assert((object)this.EntityKey != null, "entry should have a not null EntityKey");
 
            return _cache.GetObjectStateEntry(relationshipEntry.Wrapper.GetOtherEntityKey(this.EntityKey)); 
        }
 
        // Helper method to determine if the specified entityKey is in the given role and AssociationSet in this relationship entry
        // Assumes that the entityKey is on one side of the relationship.
        internal bool IsSameAssociationSetAndRole(AssociationSet associationSet, EntityKey entityKey, AssociationEndMember associationMember)
        { 
            return Object.ReferenceEquals(_entitySet, associationSet)
                && (_wrapper.GetAssociationEndMember(entityKey).Name == associationMember.Name); 
        } 

        internal object GetCurrentRelationValue(int ordinal) 
        {
            return GetCurrentRelationValue(ordinal, true);
        }
 
        private object GetCurrentRelationValue(int ordinal, bool throwException)
        { 
            ValidateRelationshipRange(ordinal); 
            ValidateState();
            if (State == EntityState.Deleted && throwException) 
            {
                throw EntityUtil.CurrentValuesDoesNotExist();
            }
            return _wrapper.GetEntityKey(ordinal); 
        }
 
        internal object GetCurrentEntityValue(StateManagerTypeMetadata metadata, int ordinal, object userObject, ObjectStateValueRecord updatableRecord) 
        {
            ValidateState(); 

            object retValue = null;
            StateManagerMemberMetadata member = metadata.Member(ordinal);
            Debug.Assert(null != member, "didn't throw ArgumentOutOfRangeException"); 

            if (!metadata.IsMemberPartofShadowState(ordinal)) 
            { // if it is not shadow state 
                retValue = member.GetValue(userObject);
 
                // Wrap the value in a record if it is a non-null complex type
                if (member.IsComplex && retValue != null)
                {
                    // need to get the new StateManagerTypeMetadata for nested /complext member 
                    switch (updatableRecord)
                    { 
                        case ObjectStateValueRecord.OriginalReadonly: 
                            retValue = new ObjectStateEntryDbDataRecord(this,
                                _cache.GetOrAddStateManagerTypeMetadata(member.CdmMetadata.TypeUsage.EdmType), retValue); 
                            break;
                        case ObjectStateValueRecord.CurrentUpdatable:
                            retValue = new ObjectStateEntryDbUpdatableDataRecord(this,
                                _cache.GetOrAddStateManagerTypeMetadata(member.CdmMetadata.TypeUsage.EdmType), retValue); 
                            break;
                        case ObjectStateValueRecord.OriginalUpdatable: 
                            retValue = new ObjectStateEntryOriginalDbUpdatableDataRecord(this, 
                                _cache.GetOrAddStateManagerTypeMetadata(member.CdmMetadata.TypeUsage.EdmType), retValue);
                            break; 
                        default:
                            Debug.Assert(false, "shouldn't happen");
                            break;
                    } 
                    // we need to pass the toplevel ordinal
                } 
            } 
#if DEBUG // performance, don't do this work in retail until shadow state is supported
            else if (userObject == _entity) 
            {
                Debug.Assert(false, "shadowstate not supported");
#if SupportShadowState
                Debug.Assert(null != _currentValues, "shadow state without values"); 
                _currentValues.TryGetValue(member.CLayerName, out retValue); // try to get it from shadow state if exists
                // we don't support CSpace only complex type 
#endif 
            }
#endif 
            return retValue ?? DBNull.Value;
        }

        // helper method used to get value of property 
        internal object GetCurrentEntityValue(string memberName)
        { 
            int ordinal = _cacheTypeMetadata.GetOrdinalforOLayerMemberName(memberName); 
            return GetCurrentEntityValue(_cacheTypeMetadata, ordinal, _entity, ObjectStateValueRecord.CurrentUpdatable);
        } 

        internal object GetOriginalEntityValue(StateManagerTypeMetadata metadata, int ordinal, object userObject, ObjectStateValueRecord updatableRecord)
        {
            // if original value is stored, then use it, otherwise use the current value from the entity 
            ValidateState();
            object retValue; 
            StateManagerMemberMetadata member = metadata.Member(ordinal); 
            if (FindOriginalValue(member, userObject, out retValue))
            { 
                // If the object is null, return DBNull.Value to be consistent with GetCurrentEntityValue
                return ((StateManagerValue)retValue).originalValue ?? DBNull.Value;
            }
            return GetCurrentEntityValue(metadata, ordinal, userObject, updatableRecord); 
        }
 
        internal object GetOriginalRelationValue(int ordinal) 
        {
            return GetCurrentRelationValue(ordinal, false); 
        }

        // Get values of key properties which doesn't already exist in passed in 'properties'
        internal void GetOtherKeyProperties(Dictionary> properties) 
        {
            Debug.Assert(properties != null); 
            Debug.Assert(_cacheTypeMetadata != null); 
            Debug.Assert(_cacheTypeMetadata.DataRecordInfo != null);
            Debug.Assert(_cacheTypeMetadata.DataRecordInfo.RecordType != null); 

            EntityType entityType = _cacheTypeMetadata.DataRecordInfo.RecordType.EdmType as EntityType;
            Debug.Assert(entityType != null, "EntityType == null");
 
            foreach (EdmMember member in entityType.KeyMembers)
            { 
                if (!properties.ContainsKey(member.Name)) 
                {
                    properties[member.Name] = new KeyValuePair(this.GetCurrentEntityValue(member.Name), new IntBox(1)); 
                }
            }
        }
 
        private void AddOriginalValue(StateManagerMemberMetadata memberMetadata, object userObject, object value)
        { 
            if (null == _originalValues) 
            {
                _originalValues = new List(); 
            }
            _originalValues.Add(new StateManagerValue(memberMetadata, userObject, value));
        }
 
        internal void CompareKeyProperties(object changed)
        { 
            Debug.Assert(changed != null, "changed object is null"); 
            Debug.Assert(!this.IsKeyEntry);
            Debug.Assert(!this.IsRelationship); 

            StateManagerTypeMetadata metadata = this._cacheTypeMetadata;

            int fieldCount = this.GetFieldCount(metadata); 
            object currentValueNew;
            object currentValueOld; 
 
            for (int i = 0; i < fieldCount; i++)
            { 
                StateManagerMemberMetadata member = metadata.Member(i);
                if (member.IsPartOfKey)
                {
                    Debug.Assert(!member.IsComplex); 

                    currentValueNew = member.GetValue(changed); 
                    currentValueOld = member.GetValue(this._entity); 

                    if (!Object.Equals(currentValueNew, currentValueOld)) 
                    {
                        throw EntityUtil.CannotModifyKeyProperty(member.CLayerName);
                    }
                } 
            }
        } 
    } 

    internal struct StateManagerValue 
    {
        internal StateManagerMemberMetadata memberMetadata;
        internal object userObject;
        internal object originalValue; 

        internal StateManagerValue(StateManagerMemberMetadata metadata, object instance, object value) 
        { 
            memberMetadata = metadata;
            userObject = instance; 
            originalValue = value;
        }
    }
 
    internal enum ObjectStateValueRecord
    { 
        OriginalReadonly = 0, 
        CurrentUpdatable = 1,
        OriginalUpdatable = 2, 
    }


    // This class is used in Referential Integrity Constraints feature. 
    // It is used to get around the problem of enumerating dictionary contents,
    // but allowing update of the value without breaking the enumerator. 
    internal sealed class IntBox 
    {
        private int val; 

        internal IntBox(int val)
        {
            this.val = val; 
        }
 
        internal int Value 
        {
            get 
            {
                return val;
            }
 
            set
            { 
                val = value; 
            }
        } 
    }
}

 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner       [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 
using System;
using System.Collections; 
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Common; 
using System.Data.Metadata.Edm;
using System.Data.Mapping; 
using System.Data.Objects.DataClasses; 
using System.Diagnostics;
using System.Reflection; 
using System.Collections.Specialized;

namespace System.Data.Objects
{ 
    internal sealed class EntityEntry : ObjectStateEntry
    { 
#if DEBUG 
        private static int _objectTypeCount; // Bid counter
        internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); 
#endif

        #region RelationshipEnd fields
        ///  
        /// Singlely-linked list of RelationshipEntry.
        /// One of the ends in the RelationshipEntry must equal this.EntityKey 
        ///  
        private RelationshipEntry _headRelationshipEnds;
 
        /// 
        /// Number of RelationshipEntry in the _relationshipEnds list.
        /// 
        private int _countRelationshipEnds; 
        #endregion
 
        #region constructors 
        internal EntityEntry(object entity, EntityKey entityKey, EntitySet extent, ObjectStateManager cache,
            StateManagerTypeMetadata typeMetadata, bool isAdded) 
            : base(entity, entityKey, extent, cache, typeMetadata, isAdded)
        {
        }
 
        // entity stub
        internal EntityEntry(EntityKey entityKey, EntitySet extent, ObjectStateManager cache, StateManagerTypeMetadata typeMetadata) 
            : base(entityKey, extent, cache, typeMetadata) 
        {
        } 
        #endregion

        #region RelationshipEnd methods
 
        /// 
        /// Add a RelationshipEntry (one of its ends must equal this.EntityKey) 
        ///  
        internal void AddRelationshipEnd(RelationshipEntry item)
        { 
#if DEBUG
            Debug.Assert(null != item, "null item");
            Debug.Assert(null != item.Wrapper, "null RelationshipWrapper");
            Debug.Assert(0 <= _countRelationshipEnds, "negative _relationshipEndCount"); 
            Debug.Assert(EntityKey.Equals(item.Wrapper.Key0) || EntityKey.Equals(item.Wrapper.Key1), "entity key doesn't match");
 
            for (RelationshipEntry current = _headRelationshipEnds; 
                 null != current;
                 current = current.GetNextRelationshipEnd(EntityKey)) 
            {
                Debug.Assert(!Object.ReferenceEquals(item, current), "RelationshipEntry already in list");
                Debug.Assert(!item.Wrapper.Equals(current.Wrapper), "RelationshipWrapper already in list");
            } 
#endif
            // the item will become the head of the list 
            // i.e. you walk the list in reverse order of items being added 
            item.SetNextRelationshipEnd(this.EntityKey, _headRelationshipEnds);
            _headRelationshipEnds = item; 
            _countRelationshipEnds++;

            Debug.Assert(_countRelationshipEnds == (new RelationshipEndEnumerable(this)).ToArray().Length, "different count");
        } 

        ///  
        /// Determines if a given relationship entry is present in the list of entries 
        /// 
        /// The entry to look for 
        /// True of the relationship end is found
        internal bool ContainsRelationshipEnd(RelationshipEntry item)
        {
            for (RelationshipEntry current = _headRelationshipEnds; 
                 null != current;
                 current = current.GetNextRelationshipEnd(EntityKey)) 
            { 
                if (object.ReferenceEquals(current, item))
                { 
                    return true;
                }
            }
            return false; 
        }
 
 
        /// 
        /// Remove a RelationshipEntry (one of its ends must equal this.EntityKey) 
        /// 
        /// 
        internal void RemoveRelationshipEnd(RelationshipEntry item)
        { 
            Debug.Assert(null != item, "removing null");
            Debug.Assert(null != item.Wrapper, "null RelationshipWrapper"); 
            Debug.Assert(1 <= _countRelationshipEnds, "negative _relationshipEndCount"); 
            Debug.Assert(EntityKey.Equals(item.Wrapper.Key0) || EntityKey.Equals(item.Wrapper.Key1), "entity key doesn't match");
 
            // walk the singly-linked list, remembering the previous node so we can remove the current node
            for (RelationshipEntry current = _headRelationshipEnds, previous = null;
                 null != current;
                 previous = current, current = current.GetNextRelationshipEnd(EntityKey)) 
            {
                if (Object.ReferenceEquals(item, current)) 
                { 
                    if (this.EntityKey.Equals(current.Key0))
                    {   // if this.EntityKey matches Key0, NextKey0 is the next element in the lsit 
                        if (null == previous)
                        {
                            _headRelationshipEnds = current.NextKey0;
                        } 
                        else
                        { 
                            previous.SetNextRelationshipEnd(EntityKey, current.NextKey0); 
                        }
                        current.NextKey0 = null; 
                    }
                    else
                    {   // if this.EntityKey matches Key1, NextKey1 is the next element in the lsit
                        Debug.Assert(EntityKey.Equals(current.Wrapper.Key1), "entity key didn't match"); 
                        if (null == previous)
                        { 
                            _headRelationshipEnds = current.NextKey1; 
                        }
                        else 
                        {
                            previous.SetNextRelationshipEnd(EntityKey, current.NextKey1);
                        }
                        current.NextKey1 = null; 
                    }
                    --_countRelationshipEnds; 
 
                    Debug.Assert(_countRelationshipEnds == (new RelationshipEndEnumerable(this)).ToArray().Length, "different count");
                    return; 
                }
                Debug.Assert(!item.Wrapper.Equals(current.Wrapper), "same wrapper, different RelationshipEntry instances");
            }
            Debug.Assert(false, "didn't remove a RelationshipEntry"); 
        }
 
        ///  
        /// Update one of the ends for the related RelationshipEntry
        ///  
        /// the EntityKey the relationship should currently have
        /// if promoting entity stub to full entity
        internal void UpdateRelationshipEnds(EntityKey oldKey, EntityEntry promotedEntry)
        { 
            Debug.Assert(null != (object)oldKey && oldKey.IsTemporary, "bad oldKey");
            Debug.Assert(!EntityKey.IsTemporary, "bad newKey, should not be temporary"); 
            Debug.Assert(!Object.ReferenceEquals(this, promotedEntry), "shouldn't be same reference"); 

            // traverse the list to update one of the ends in the relationship entry 
            int count = 0;
            RelationshipEntry next = _headRelationshipEnds;
            while (null != next)
            { 
                // get the next relationship end before we change the key of current relationship end
                RelationshipEntry current = next; 
                next = next.GetNextRelationshipEnd(oldKey); 

                // update the RelationshipEntry from the temporary key to real key 
                current.ChangeRelatedEnd(oldKey, EntityKey);

                // If we have a promoted entry, copy the relationship entries to the promoted entry
                // only if the promoted entry doesn't already know about that particular relationship entry 
                // This can be the case with self referencing entities
                if (null != promotedEntry && !promotedEntry.ContainsRelationshipEnd(current)) 
                {   // all relationship ends moved to new promotedEntry 
                    promotedEntry.AddRelationshipEnd(current);
                } 
                ++count;
            }
            Debug.Assert(count == _countRelationshipEnds, "didn't traverse all relationships");
            if (null != promotedEntry) 
            {   // cleanup existing (dead) entry to reduce confusion
                _headRelationshipEnds = null; 
                _countRelationshipEnds = 0; 
            }
        } 

        #region Enumerable and Enumerator
        internal RelationshipEndEnumerable GetRelationshipEnds()
        { 
            return new RelationshipEndEnumerable(this);
        } 
 
        /// 
        /// An enumerable so that EntityEntry doesn't implement it 
        /// 
        internal struct RelationshipEndEnumerable : IEnumerable, IEnumerable
        {
            internal static readonly RelationshipEntry[] EmptyRelationshipEntryArray = new RelationshipEntry[0]; 
            private readonly EntityEntry _entityEntry;
 
            internal RelationshipEndEnumerable(EntityEntry entityEntry) 
            {   // its okay if entityEntry is null
                _entityEntry = entityEntry; 
            }
            public RelationshipEndEnumerator GetEnumerator()
            {
                return new RelationshipEndEnumerator(_entityEntry); 
            }
            IEnumerator IEnumerable.GetEnumerator() 
            { 
                return GetEnumerator();
            } 
            IEnumerator IEnumerable.GetEnumerator()
            {
                Debug.Assert(false, "dead code, don't box the RelationshipEndEnumerable");
                return GetEnumerator(); 
            }
            IEnumerator IEnumerable.GetEnumerator() 
            { 
                Debug.Assert(false, "dead code, don't box the RelationshipEndEnumerable");
                return GetEnumerator(); 
            }

            /// 
            /// Convert the singly-linked list into an Array 
            /// 
            internal RelationshipEntry[] ToArray() 
            { 
                RelationshipEntry[] list = null;
                if ((null != _entityEntry) && (0 < _entityEntry._countRelationshipEnds)) 
                {
                    RelationshipEntry relationshipEnd = _entityEntry._headRelationshipEnds;
                    list = new RelationshipEntry[_entityEntry._countRelationshipEnds];
                    for (int i = 0; i < list.Length; ++i) 
                    {
                        Debug.Assert(null != relationshipEnd, "count larger than list"); 
                        Debug.Assert(_entityEntry.EntityKey.Equals(relationshipEnd.Key0) || _entityEntry.EntityKey.Equals(relationshipEnd.Key1), "entity key mismatch"); 
                        list[i] = relationshipEnd;
 
                        relationshipEnd = relationshipEnd.GetNextRelationshipEnd(_entityEntry.EntityKey);
                    }
                    Debug.Assert(null == relationshipEnd, "count smaller than list");
                } 
                return list ?? EmptyRelationshipEntryArray;
            } 
        } 

        ///  
        /// An enumerator to walk the RelationshipEntry linked-list
        /// 
        internal struct RelationshipEndEnumerator : IEnumerator, IEnumerator
        { 
            private readonly EntityEntry _entityEntry;
            private RelationshipEntry _current; 
 
            internal RelationshipEndEnumerator(EntityEntry entityEntry)
            { 
                _entityEntry = entityEntry;
                _current = null;
            }
            public RelationshipEntry Current 
            {
                get { return _current; } 
            } 
            IEntityStateEntry IEnumerator.Current
            { 
                get { return _current; }
            }
            object IEnumerator.Current
            { 
                get
                { 
                    Debug.Assert(false, "dead code, don't box the RelationshipEndEnumerator"); 
                    return _current;
                } 
            }
            public void Dispose()
            {
            } 
            public bool MoveNext()
            { 
                if (null != _entityEntry) 
                {
                    if (null == _current) 
                    {
                        _current = _entityEntry._headRelationshipEnds;
                    }
                    else 
                    {
                        _current = _current.GetNextRelationshipEnd(_entityEntry.EntityKey); 
                    } 
                }
                return (null != _current); 
            }
            public void Reset()
            {
                Debug.Assert(false, "not implemented"); 
            }
        } 
        #endregion 
        #endregion
    } 

    internal sealed class RelationshipEntry : ObjectStateEntry
    {
#if DEBUG 
        private static int _objectTypeCount; // Bid counter
        internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); 
#endif 

        #region Linked list of related relationships 
        private RelationshipEntry _nextKey0;
        private RelationshipEntry _nextKey1;
        #endregion
 
        // relationship
        internal RelationshipEntry(ObjectStateManager cache, EntityState state, RelationshipWrapper wrapper) 
            : base(cache, state, wrapper) 
        {
        } 

        #region RelationshipEnds as singly-linked list

        internal EntityKey Key0 { get { return Wrapper.Key0; } } 
        internal EntityKey Key1 { get { return Wrapper.Key1; } }
 
        ///  
        /// Update one of the ends of the relationship
        ///  
        internal void ChangeRelatedEnd(EntityKey oldKey, EntityKey newKey)
        {
            if (oldKey.Equals(Key0))
            { 
                if (oldKey.Equals(Key1))
                {   // self-reference 
                    Wrapper = new RelationshipWrapper(Wrapper.AssociationSet, newKey); 
                }
                else 
                {
                    Wrapper = new RelationshipWrapper(Wrapper, 0, newKey);
                }
            } 
            else
            { 
                Wrapper = new RelationshipWrapper(Wrapper, 1, newKey); 
            }
        } 

        internal RelationshipEntry GetNextRelationshipEnd(EntityKey entityKey)
        {
            Debug.Assert(null != (object)entityKey, "null EntityKey"); 
            Debug.Assert(entityKey.Equals(Key0) || entityKey.Equals(Key1), "EntityKey mismatch");
            return (entityKey.Equals(Key0) ? NextKey0 : NextKey1); 
        } 

        internal void SetNextRelationshipEnd(EntityKey entityKey, RelationshipEntry nextEnd) 
        {
            Debug.Assert(null != (object)entityKey, "null EntityKey");
            Debug.Assert(entityKey.Equals(Key0) || entityKey.Equals(Key1), "EntityKey mismatch");
            if (entityKey.Equals(Key0)) 
            {
                NextKey0 = nextEnd; 
            } 
            else
            { 
                NextKey1 = nextEnd;
            }
        }
 
        /// 
        /// Use when EntityEntry.EntityKey == this.Wrapper.Key0 
        ///  
        internal RelationshipEntry NextKey0
        { 
            get { return _nextKey0; }
            set { _nextKey0 = value; }

        } 

        ///  
        /// Use when EntityEntry.EntityKey == this.Wrapper.Key1 
        /// 
        internal RelationshipEntry NextKey1 
        {
            get { return _nextKey1; }
            set { _nextKey1 = value; }
        } 
        #endregion
    } 
 
    // Detached - nothing
 
    // Added - _entity & _currentValues only for shadowState

    // Unchanged - _entity & _currentValues only for shadowState
    // Unchanged -> Deleted - _entity & _currentValues only for shadowState 

    // Modified - _currentValues & _modifiedFields + _originalValues only on change 
    // Modified -> Deleted - _currentValues & _modifiedFields + _originalValues only on change 

    ///  
    /// Represets either a entity, entity stub or relationship
    /// 
    public abstract class ObjectStateEntry : IEntityStateEntry, IEntityChangeTracker
    { 
        #region common entry fields
        private ObjectStateManager _cache; 
        private EntityState _state; 
        #endregion
 
        #region entity entry fields
        private StateManagerTypeMetadata _cacheTypeMetadata;
        private EntitySetBase _entitySet;   // !null if IsKeyEntry or Entity (or IsRelationship)
        private EntityKey _entityKey;       // !null if IsKeyEntry or Entity 
        private object _entity;             // null if IsKeyEntry
 
        // entity entry change tracking 
        private BitArray _modifiedFields;  // only and always exists if state is Modified or after Delete() on Modified
        private List _originalValues; // only exists if _modifiedFields has a true-bit 
        #endregion

        #region relationship entry fields
        private RelationshipWrapper _wrapper;   // !null if IsRelationship 
        #endregion
 
        // ObjectStateEntry will not be detached and creation will be handled from ObjectStateManager 
        internal ObjectStateEntry(object entity, EntityKey entityKey, EntitySet extent, ObjectStateManager cache,
            StateManagerTypeMetadata typeMetadata, bool isAdded) 
        {
            Debug.Assert(entity != null, "entity cannot be null.");
            Debug.Assert(extent != null, "extent cannot be null.");
            Debug.Assert(cache != null, "cache cannot be null."); 
            Debug.Assert(typeMetadata != null, "typeMetadata cannot be null.");
            Debug.Assert((null == (object)entityKey) || (entityKey.EntitySetName == extent.Name), "different extent"); 
 
            _cache = cache;
            _entity = entity; 
            _entitySet = extent;
            _cacheTypeMetadata = typeMetadata;
            _entityKey = entityKey;
            this.State = isAdded ? EntityState.Added : EntityState.Unchanged; 
        }
 
        // ObjectStateEntry for Entity Keys (i.e. Span Stubs) 
        internal ObjectStateEntry(EntityKey entityKey, EntitySet extent, ObjectStateManager cache, StateManagerTypeMetadata typeMetadata)
        { 
            Debug.Assert((object)entityKey != null, "entityKey cannot be null.");
            Debug.Assert(extent != null, "extent cannot be null.");
            Debug.Assert(cache != null, "cache cannot be null.");
            Debug.Assert(typeMetadata != null, "typeMetadata cannot be null."); 
            Debug.Assert(entityKey.EntitySetName == extent.Name, "different extent");
 
            _cache = cache; 
            _entityKey = entityKey;
            _entitySet = extent; 
            _cacheTypeMetadata = typeMetadata;

            State = EntityState.Unchanged;
        } 

        ///  
        /// for RelationshipEntry 
        /// 
        internal ObjectStateEntry(ObjectStateManager cache, EntityState state, RelationshipWrapper wrapper) 
        {
            Debug.Assert(null != cache, "null ObjectStateManager");
            Debug.Assert(EntityState.Added == state ||
                         EntityState.Unchanged == state || 
                         EntityState.Deleted == state,
                         "invalid EntityState"); 
            Debug.Assert(null != wrapper, "null RelationshipWrapper"); 
            _cache = cache;
            _entitySet = wrapper.AssociationSet; 
            _wrapper = wrapper;
            _state = state;
        }
 
        IEntityStateManager IEntityStateEntry.StateManager
        { 
            get 
            {
                return (IEntityStateManager)this.ObjectStateManager; 
            }
        }

        ///  
        /// ObjectStateManager property of ObjectStateEntry.
        ///  
        ///  
        ///  ObjectStateManager 
        public ObjectStateManager ObjectStateManager 
        {
            get
            {
                ValidateState(); 
                return _cache;
            } 
        } 
        /// 
        /// Entity property of ObjectStateEntry. 
        /// 
        /// 
        ///  The entity encapsulated by this entry. 
        public object Entity 
        {
            get 
            { 
                ValidateState();
                return _entity; 
            }
        }

        ///  
        /// The EntityKey associated with the ObjectStateEntry
        ///  
        public EntityKey EntityKey 
        {
            get 
            {
                ValidateState();
                return _entityKey;
            } 
            internal set
            { 
                _entityKey = value; 
            }
        } 

        ///  Extent property of ObjectStateEntry. 
        /// 
        ///  Extent  
        public EntitySetBase EntitySet
        { 
            get 
            {
                ValidateState(); 
                return _entitySet;
            }
        }
 
        /// 
        /// Determines if this ObjectStateEntry represents a relationship 
        ///  
        public bool IsRelationship
        { 
            get
            {
                Debug.Assert((null != _wrapper && null == _entity && null == (object)_entityKey) ||
                             (null == _wrapper && (null != _entity || null != (object)_entityKey)) || 
                             (EntityState.Detached == _state),
                             "IsEntity, IsKeyEntry, IsRelationship mixup"); 
 
                // wrapper is always reset to null when the entry is Detached, so if it's not null, this has to be a relationship entry
                // If the wrapper is null, it may be a different kind of entry or may be detached. For consistency with rest of public 
                // API, throw if it's Detached.
                if (null == _wrapper)
                {
                    ValidateState(); 
                    return false;
                } 
                else 
                {
                    return true; 
                }
            }
        }
 
        internal bool IsKeyEntry
        { 
            get 
            {
                Debug.Assert((null != _wrapper && null == _entity && null == (object)_entityKey) || 
                             (null == _wrapper && (null != _entity || null != (object)_entityKey)) ||
                             (EntityState.Detached == _state),
                             "IsEntity, IsKeyEntry, IsRelationship mixup");
                return ((null == _entity) && (null != (object)_entityKey)); // if there is no entity but there is a key, then it is a key entry 
            }
        } 
 
        // must explicitly implement this because interface is internal & so is the property on the
        // class itself -- apparently the compiler won't let anything marked as internal be part of 
        // an interface (even if the interface is also internal)
        bool IEntityStateEntry.IsKeyEntry
        {
            get 
            {
                return this.IsKeyEntry; 
            } 
        }
 
        internal int GetFieldCount(StateManagerTypeMetadata metadata)
        {
            if (!IsRelationship)
            { 
                return metadata.FieldCount;
            } 
            return _wrapper.AssociationEndMembers.Count; 
        }
 
        internal void PromoteKeyEntry(object entity, IExtendedDataRecord shadowValues, StateManagerTypeMetadata typeMetadata)
        {
            Debug.Assert(entity != null, "entity cannot be null.");
            Debug.Assert(IsKeyEntry, "ObjectStateEntry should be a key."); 
            Debug.Assert(typeMetadata != null, "typeMetadata cannot be null.");
 
            _entity = entity; 

            // Allow updating of cached metadata because the actual entity might be a derived type 
            _cacheTypeMetadata = typeMetadata;

#if DEBUG   // performance, don't do this work in retail until shadow state is supported
            if (shadowValues != null) 
            {
                // shadowState always  coms from materializer, just copy the shadowstate values 
                Debug.Assert(shadowValues.DataRecordInfo.RecordType.EdmType.Equals(_cacheTypeMetadata.CdmMetadata.EdmType), "different cspace metadata instance"); 
                for (int ordinal = 0; ordinal < _cacheTypeMetadata.FieldCount; ordinal++)
                { 
                    if (_cacheTypeMetadata.IsMemberPartofShadowState(ordinal))
                    {
                        Debug.Assert(false, "shadowstate not supported");
                    } 
                }
            } 
#endif 
        }
 
        /// 
        /// Turns this entry into a key entry (SPAN stub).
        /// 
        internal void DegradeEntry() 
        {
            Debug.Assert(!IsKeyEntry); 
            Debug.Assert(!IsRelationship); 
            Debug.Assert((object)_entityKey != null);
 
            _entityKey = EntityKey; //Performs validation.

            EntityUtil.SetChangeTrackerOntoEntity(_entity, (IEntityChangeTracker)null);
 
            _modifiedFields = null;
            _originalValues = null; 
 
            // we don't want temporary keys to exist outside of the context
            if (State == EntityState.Added) 
            {
                EntityUtil.SetKeyOntoEntity(_entity, null);
                _entityKey = null;
            } 

            if (State != EntityState.Unchanged) 
            { 
                _cache.ChangeState(this, this.State, EntityState.Unchanged);
                State = EntityState.Unchanged; 
            }

            _cache.RemoveEntryFromKeylessStore(_entity);
 
            RelationshipManager relationshipManager = EntityUtil.GetRelationshipManager(_entity);
            if (relationshipManager != null) 
            { 
                relationshipManager.DetachContext();
            } 

            object degradedEntity = _entity;
            _entity = null;
            _cache.OnObjectStateManagerChanged(CollectionChangeAction.Remove, degradedEntity); 

            Debug.Assert(IsKeyEntry); 
        } 

 

        /// 
        /// State property of ObjectStateEntry.
        ///  
        /// 
        ///  DataRowState  
        public EntityState State 
        {
            get 
            {
                return _state;
            }
            internal set 
            {
                _state = value; 
            } 
        }
 
        /// 
        /// Original values of entity
        /// 
        ///  
        ///  DbDataRecord 
        [DebuggerBrowsable(DebuggerBrowsableState.Never)] // don't have debugger view expand this 
        public DbDataRecord OriginalValues 
        {
            get 
            {
                ValidateState();
                if (_state == EntityState.Added)
                { 
                    throw EntityUtil.OriginalValuesDoesNotExist();
                } 
 
                if (IsRelationship)
                { 
                    return new ObjectStateEntryDbDataRecord(this);
                }
                else if (IsKeyEntry)
                { 
                    throw EntityUtil.CannotAccessKeyEntryValues();
                } 
                else 
                {
                    return new ObjectStateEntryDbDataRecord(this, _cacheTypeMetadata, _entity); 
                }
            }
        }
 
        [DebuggerBrowsable(DebuggerBrowsableState.Never)] // don't have debugger view expand this
        internal CurrentValueRecord EditableOriginalValues 
        { 
            get
            { 
                Debug.Assert(!IsRelationship, "should not edit original relationship");
                Debug.Assert(!IsKeyEntry, "should not edit original key entry");
                Debug.Assert(EntityState.Modified == State ||
                             EntityState.Deleted == State, 
                             "only expecting Modified or Deleted state");
 
                return new ObjectStateEntryOriginalDbUpdatableDataRecord(this, _cacheTypeMetadata, _entity); 
            }
        } 

        /// 
        /// Current values of entity/ DataRow
        ///  
        /// 
        ///  DbUpdatableDataRecord  
        [DebuggerBrowsable(DebuggerBrowsableState.Never)] // don't have debugger view expand this 
        public CurrentValueRecord CurrentValues
        { 
            get
            {
                ValidateState();
                if (_state == EntityState.Deleted) 
                {
                    throw EntityUtil.CurrentValuesDoesNotExist(); 
                } 

                if (IsRelationship) 
                {
                    return new ObjectStateEntryDbUpdatableDataRecord(this);
                }
                else if (IsKeyEntry) 
                {
                    throw EntityUtil.CannotAccessKeyEntryValues(); 
                } 
                else
                { 
                    return new ObjectStateEntryDbUpdatableDataRecord(this, _cacheTypeMetadata, _entity);
                }
            }
        } 

        internal RelationshipWrapper Wrapper 
        { 
            get
            { 
                return _wrapper;
            }
            set
            { 
                Debug.Assert(IsRelationship, "Non Relation ObjectStateEntry can not have RelationshipWrapper");
                Debug.Assert(null != value, "don't set wrapper to null"); 
                _wrapper = value; 
            }
        } 

        internal void AttachObjectStateManagerToEntity()
        {
            // This method should only be called in cases where we really have an entity to attach to 
            Debug.Assert(_entity != null, "Cannot attach a null entity to the state manager");
 
            EntityUtil.SetChangeTrackerOntoEntity(_entity, this); 
        }
 
        internal void DetachObjectStateManagerFromEntity()
        {
            // This method can be called on relationship entries where there is no entity
            if (!IsRelationship && !IsKeyEntry) // _entity is not null. 
            {
                EntityUtil.SetChangeTrackerOntoEntity(_entity, (IEntityChangeTracker)null); 
 
                RelationshipManager relationshipManager = EntityUtil.GetRelationshipManager(_entity);
                if (relationshipManager != null) 
                {
                    relationshipManager.DetachContext();
                }
 
                //Entry's this._entityKey is set to null at the caller, maintaining consistency between entityWithKey.EntityKey and this.EntityKey
                EntityUtil.SetKeyOntoEntity(_entity, null); 
            } 
        }
 
        internal Type GetFieldType(int ordinal, StateManagerTypeMetadata metadata)
        {
            if (!IsRelationship)
            { 
                return metadata.GetFieldType(ordinal);
            } 
            else 
            {
                return typeof(EntityKey); // this is given By Design 
            }
        }
        internal string GetCLayerName(int ordinal, StateManagerTypeMetadata metadata)
        { 
            if (!IsRelationship)
            { 
                return metadata.CLayerMemberName(ordinal); 
            }
            else 
            {
                ValidateRelationshipRange(ordinal);
                return _wrapper.AssociationEndMembers[ordinal].Name;
            } 
        }
        internal int GetOrdinalforCLayerName(string name, StateManagerTypeMetadata metadata) 
        { 
            if (!IsRelationship)
            { 
                return metadata.GetOrdinalforCLayerMemberName(name);
            }
            else
            { 
                AssociationEndMember endMember;
                ReadOnlyMetadataCollection endMembers = _wrapper.AssociationEndMembers; 
                if (endMembers.TryGetValue(name, false, out endMember)) 
                {
                    return endMembers.IndexOf(endMember); 
                }
                return -1;
            }
        } 

        ///  
        /// API to accept the current values as original values and  mark the entity as Unchanged. 
        /// 
        ///  
        /// 
        public void AcceptChanges()
        {
            ValidateState(); 

            Debug.Assert(!IsKeyEntry || State == EntityState.Unchanged, "Key ObjectStateEntries must always be unchanged."); 
 
            switch (State)
            { 
                case EntityState.Deleted:
                    CascadeAcceptChanges();
                    // Current entry could be already detached if this is relationship entry and if one end of relationship was a KeyEntry
                    if (_cache != null) 
                    {
                        _cache.ChangeState(this, EntityState.Deleted, EntityState.Detached); 
                    } 
                    break;
                case EntityState.Added: 
                    if (!IsRelationship)
                    {
                        // If this entry represents an entity, perform key fixup.
                        Debug.Assert(Entity != null, "Non-relationship entries should have a non-null entity."); 
                        Debug.Assert((object)_entityKey != null, "All entities in the state manager should have a non-null EntityKey.");
                        Debug.Assert(_entityKey.IsTemporary, "All entities in the Added state should have a temporary EntityKey."); 
 
                        // Retrieve referential constraint properties from Principal entities (possibly recursively)
                        // and check referential constraint properties in the Dependent entities (1 level only) 
                        this.RetrieveAndCheckReferentialConstraintValues();

                        _cache.FixupKey((EntityEntry)this);
                    } 
                    else
                    { 
                        _cache.ChangeState(this, EntityState.Added, EntityState.Unchanged); 
                    }
                    _modifiedFields = null; 
                    _originalValues = null;
                    State = EntityState.Unchanged;
                    break;
                case EntityState.Modified: 
                    _cache.ChangeState(this, EntityState.Modified, EntityState.Unchanged);
                    _modifiedFields = null; 
                    _originalValues = null; 
                    State = EntityState.Unchanged;
                    break; 
                case EntityState.Unchanged:
                    break;
            }
        } 

        private void CascadeAcceptChanges() 
        { 
            if (!IsRelationship)
            { 
                foreach (ObjectStateEntry entry in _cache.CopyOfRelationshipsByKey(EntityKey))
                {
                    Debug.Assert(entry.IsRelationship, "It should be a Relationship ObjectStateEntry");
 
                    // CascadeAcceptChanges is only called on Entity ObjectStateEntry when it is
                    // in deleted state. Entity is in deleted state therefore for all related Relationship 
                    // cache entries only valid state is Deleted. 
                    Debug.Assert(entry.State == EntityState.Deleted, "Relationship ObjectStateEntry should be in deleted state");
                    entry.AcceptChanges(); 
                }
            }
            else
            { 
                DeleteUnnecessaryKeyEntries();
            } 
        } 

        private void DeleteUnnecessaryKeyEntries() 
        {
            Debug.Assert(this.IsRelationship, "entry must be relationship entry");

            // We need to check to see if the ends of the relationship are key entries. 
            // If they are, and nothing else refers to them then the key entry should be removed.
            for (int i = 0; i < 2; i++) 
            { 
                EntityKey entityKey = this.GetCurrentRelationValue(i, false) as EntityKey;
                ObjectStateEntry relatedEntry = _cache.GetObjectStateEntry(entityKey); 
                if (relatedEntry.IsKeyEntry)
                {
                    bool foundRelationship = false;
                    // count the number of relationships this key entry is part of 
                    // if there aren't any, then the relationship should be deleted
                    foreach (ObjectStateEntry relationshipEntry in _cache.FindRelationshipsByKey(entityKey)) 
                    { 
                        // only count relationships that are not the one we are currently deleting (i.e. this)
                        if (relationshipEntry != this) 
                        {
                            foundRelationship = true;
                            break;
                        } 
                    }
                    if (!foundRelationship) 
                    { 
                        // Nothing is refering to this key entry, so it should be removed from the cache
                        _cache.DeleteKeyEntry(relatedEntry); 
                        // We assume that only one end of relationship can be a key entry,
                        // so we can break the loop
                        break;
                    } 
                }
            } 
        } 

        // Detaches related relationship entries if other ends of these relationships are key entries. 
        // Key entry from the other side of the relationship is removed if is not related to other entries.
        private void DetachRelationshipsEntries(RelationshipManager relationshipManager)
        {
            Debug.Assert(relationshipManager != null, "Unexpected null RelationshipManager"); 
            Debug.Assert(!this.IsKeyEntry, "Should only be detaching relationships with key entries if the source is not a key entry");
 
            foreach (ObjectStateEntry relationshipEntry in _cache.CopyOfRelationshipsByKey(EntityKey)) 
            {
                // Get state entry for other side of the relationship 
                EntityKey targetKey = relationshipEntry.Wrapper.GetOtherEntityKey(EntityKey);
                Debug.Assert((object)targetKey != null, "EntityKey not on either side of relationship as expected");

                ObjectStateEntry relatedEntry = _cache.GetObjectStateEntry(targetKey); 
                if (relatedEntry.IsKeyEntry)
                { 
                    // This must be an EntityReference, so set the DetachedEntityKey if the relationship is currently Added or Unchanged 
                    // devnote: This assumes that we are in the middle of detaching the entity associated with this state entry, because
                    //          we don't always want to preserve the EntityKey for every detached relationship, if the source entity itself isn't being detached 
                    if (relationshipEntry.State != EntityState.Deleted)
                    {
                        AssociationEndMember targetMember = relationshipEntry.Wrapper.GetAssociationEndMember(targetKey);
                        // devnote: Since we know the target end of this relationship is a key entry, it has to be a reference, so just cast 
                        EntityReference entityReference = (EntityReference) relationshipManager.GetRelatedEnd(targetMember.DeclaringType.FullName, targetMember.Name);
                        entityReference.DetachedEntityKey = targetKey; 
                    } 
                    // else do nothing -- we can't null out the key for Deleted state, because there could be other relationships with this same source in a different state
 
                    // Remove key entry if necessary
                    relationshipEntry.DeleteUnnecessaryKeyEntries();
                    // Remove relationship entry
                    relationshipEntry.DetachRelationshipEntry(); 
                }
                // else the other end is a real entity, we will deal with that separately 
            } 
        }
 

        // Retrieve referential constraint properties from Principal entities (possibly recursively)
        // and check referential constraint properties in the Dependent entities (1 level only)
        private void RetrieveAndCheckReferentialConstraintValues() 
        {
            RelationshipManager relationshipManager = EntityUtil.GetRelationshipManager(_entity); 
            if (relationshipManager != null) 
            {
                // Find key property names which are part of referential integrity constraints 
                List propertiesToRetrieve;  // names of properties which should be retrieved from Principal entities
                bool propertiesToCheckExist;        // true iff there are properties which should be checked in dependent entities

                // Get RI property names from metadata 
                relationshipManager.FindNamesOfReferentialConstraintProperties(out propertiesToRetrieve, out propertiesToCheckExist);
 
                // Do not try to retrieve RI properties if entity doesn't participate in any RI Constraints 
                if (propertiesToRetrieve != null)
                { 
                    // Retrieve key values from related entities
                    Dictionary> properties;

                    // Create HashSet to store references to already visited entities, used to detect circular references 
                    HashSet visited = new HashSet();
 
                    relationshipManager.RetrieveReferentialConstraintProperties(out properties, false /*includeOwnValues*/, visited); 

                    // Update properties 
                    foreach (KeyValuePair> pair in properties)
                    {
                        this.SetCurrentEntityValue(pair.Key /*name*/, pair.Value.Key /*value*/);
                    } 
                }
 
                if (propertiesToCheckExist) 
                {
                    // Compare properties of current entity with properties of the dependent entities 
                    this.CheckReferentialConstraintPropertiesInDependents();
                }
            }
        } 

 
        internal void RetrieveReferentialConstraintPropertiesFromKeyEntries(Dictionary> properties) 
        {
            string thisRole; 
            AssociationSet association;

            // Iterate through related relationship entries
            foreach (ObjectStateEntry relationshipEntry in _cache.FindRelationshipsByKey(EntityKey)) 
            {
                ObjectStateEntry otherEnd = this.GetOtherEndOfRelationship(relationshipEntry); 
 
                // We only try to retrieve properties from key entries
                if (otherEnd.IsKeyEntry) 
                {
                    association = (AssociationSet)relationshipEntry.EntitySet;
                    Debug.Assert(association != null, "relationship is not an association");
 
                    // Iterate through referential constraints of the association of the relationship
                    // NOTE PERFORMANCE This collection in current stack can have 0 or 1 elements 
                    foreach (ReferentialConstraint constraint in association.ElementType.ReferentialConstraints) 
                    {
                        thisRole = this.GetAssociationEndMember(relationshipEntry).Name; 

                        // Check if curent entry is a dependent end of the referential constraint
                        if (constraint.ToRole.Name == thisRole)
                        { 
                            Debug.Assert(!otherEnd.EntityKey.IsTemporary, "key of key entry can't be temporary");
                            IList otherEndKeyValues = otherEnd.EntityKey.EntityKeyValues; 
                            Debug.Assert(otherEndKeyValues != null, "key entry must have key values"); 

                            // NOTE PERFORMANCE Number of key properties is supposed to be "small" 
                            foreach (EntityKeyMember pair in otherEndKeyValues)
                            {
                                for (int i = 0; i < constraint.FromProperties.Count; ++i)
                                { 
                                    if (constraint.FromProperties[i].Name == pair.Key)
                                    { 
                                        ObjectStateEntry.AddOrIncreaseCounter(properties, constraint.ToProperties[i].Name, pair.Value); 
                                    }
                                } 
                            }
                        }
                    }
                } 
            }
        } 
 
        internal static void AddOrIncreaseCounter(Dictionary> properties, string propertyName, object propertyValue)
        { 
            Debug.Assert(properties != null);
            Debug.Assert(propertyName != null);
            Debug.Assert(propertyValue != null);
 
            if (properties.ContainsKey(propertyName))
            { 
                // If this property already exists in the dictionary, check if value is the same then increase the counter 

                KeyValuePair valueCounterPair = properties[propertyName]; 

                if (!valueCounterPair.Key.Equals(propertyValue))
                    throw EntityUtil.InconsistentReferentialConstraintProperties();
                else 
                    valueCounterPair.Value.Value = valueCounterPair.Value.Value + 1;
            } 
            else 
            {
                // If property doesn't exist in the dictionary - add new entry with pair 
                properties[propertyName] = new KeyValuePair(propertyValue, new IntBox(1));
            }
        }
 

        // Check if related dependent entities contain proper property values 
        // Only entities in Unchanged and Modified state are checked (including KeyEntries) 
        private void CheckReferentialConstraintPropertiesInDependents()
        { 
            string thisRole;
            AssociationSet association;

            // Iterate through related relationship entries 
            foreach (ObjectStateEntry relationshipEntry in _cache.FindRelationshipsByKey(EntityKey))
            { 
                ObjectStateEntry otherEnd = this.GetOtherEndOfRelationship(relationshipEntry); 

                // We only check entries which are in Unchanged or Modified state 
                // (including KeyEntries which are always in Unchanged State)
                if (otherEnd.State == EntityState.Unchanged || otherEnd.State == EntityState.Modified)
                {
                    association = (AssociationSet)relationshipEntry.EntitySet; 
                    Debug.Assert(association != null, "relationship is not an association");
 
                    // Iterate through referential constraints of the association of the relationship 
                    // NOTE PERFORMANCE This collection in current stack can have 0 or 1 elements
                    foreach (ReferentialConstraint constraint in association.ElementType.ReferentialConstraints) 
                    {
                        thisRole = this.GetAssociationEndMember(relationshipEntry).Name;

                        // Check if curent entry is a principal end of the referential constraint 
                        if (constraint.FromRole.Name == thisRole)
                        { 
                            Debug.Assert(!otherEnd.EntityKey.IsTemporary, "key of Unchanged or Modified entry can't be temporary"); 
                            IList otherEndKeyValues = otherEnd.EntityKey.EntityKeyValues;
                            // NOTE PERFORMANCE Number of key properties is supposed to be "small" 
                            foreach (EntityKeyMember pair in otherEndKeyValues)
                            {
                                for (int i = 0; i < constraint.FromProperties.Count; ++i)
                                { 
                                    if (constraint.ToProperties[i].Name == pair.Key)
                                    { 
                                        if (!this.GetCurrentEntityValue(constraint.FromProperties[i].Name).Equals(pair.Value)) 
                                        {
                                            throw EntityUtil.InconsistentReferentialConstraintProperties(); 
                                        }
                                    }
                                }
                            } 
                        }
                    } 
                } 
            }
        } 

        /// 
        /// API to mark the entity deleted. if entity is in added state, it will be detached
        ///  
        /// 
        ///   
        public void Delete() 
        {
            // doFixup flag is used for Cache and Collection & Ref consistency 
            // When some entity is deleted if "doFixup" is true then Delete method
            // calls the Collection & Ref code to do the necessary fix-ups.
            // "doFixup" equals to False is only called from EntityCollection & Ref code
            Delete(/*doFixup*/true); 
        }
 
        //"doFixup" equals to False is called from EntityCollection & Ref code only 
        internal void Delete(bool doFixup)
        { 
            ValidateState();

            if (IsKeyEntry)
            { 
                throw EntityUtil.CannotCallDeleteOnKeyEntry();
            } 
 
            if (IsRelationship && doFixup)
            { 
                if (State != EntityState.Deleted)  //for deleted ObjectStateEntry its a no-op
                {
                    //Find two ends of the relationship
                    ObjectStateEntry entry1 = _cache.GetObjectStateEntry((EntityKey)GetCurrentRelationValue(0)); 
                    IEntityWithRelationships entity1 = entry1.Entity as IEntityWithRelationships;
                    ObjectStateEntry entry2 = _cache.GetObjectStateEntry((EntityKey)GetCurrentRelationValue(1)); 
                    IEntityWithRelationships entity2 = entry2.Entity as IEntityWithRelationships; 

                    // If one end of the relationship is a KeyEntry, entity1 or entity2 is null. 
                    // It is not possible that both ends of relationship are KeyEntries.
                    if (entity1 != null && entity2 != null)
                    {
                        //Create navigation 
                        ReadOnlyMetadataCollection endMembers = _wrapper.AssociationEndMembers;
                        string role1 = endMembers[0].Name; 
                        string role2 = endMembers[1].Name; 
                        string relationshipName = ((AssociationSet)_entitySet).ElementType.FullName;
                        RelationshipNavigation navigation = new RelationshipNavigation(relationshipName, role1, role2); 

                        entity1.RelationshipManager.RemoveEntity(navigation, entity2);
                    }
                    else 
                    {
                        // One end of relationship is a KeyEntry, figure out which one is the real entity and get its RelationshipManager 
                        // so we can update the DetachedEntityKey on the EntityReference associated with this relationship 
                        EntityKey targetKey = null;
                        RelationshipManager relationshipManager = null; 
                        if (entity1 == null)
                        {
                            targetKey = entry1.EntityKey;
                            relationshipManager = EntityUtil.GetRelationshipManager(entity2); 
                        }
                        else 
                        { 
                            targetKey = entry2.EntityKey;
                            relationshipManager = EntityUtil.GetRelationshipManager(entity1); 
                        }

                        // Clear the detachedEntityKey as well. In cases where we have to fix up the detachedEntityKey, we will not always be able to detect
                        // if we have *only* a Deleted relationship for a given entity/relationship/role, so clearing this here will ensure that 
                        // even if no other relationships are added, the key value will still be correct and we won't accidentally pick up an old value.
 
                        // devnote: Since we know the target end of this relationship is a key entry, it has to be a reference, so just cast 
                        AssociationEndMember targetMember = this.Wrapper.GetAssociationEndMember(targetKey);
                        EntityReference entityReference = (EntityReference)relationshipManager.GetRelatedEnd(targetMember.DeclaringType.FullName, targetMember.Name); 
                        entityReference.DetachedEntityKey = null;

                        // Now update the state
                        if (this.State == EntityState.Added) 
                        {
                            // Remove key entry if necessary 
                            DeleteUnnecessaryKeyEntries(); 
                            // Remove relationship entry
                            // devnote: Using this method instead of just changing the state because the entry 
                            //          may have already been detached along with the key entry above. However,
                            //          if there were other relationships using the key, it would not have been deleted.
                            DetachRelationshipEntry();
                        } 
                        else
                        { 
                            // Non-added entries should be deleted 
                            _cache.ChangeState(this, this.State, EntityState.Deleted);
                            State = EntityState.Deleted; 
                        }
                    }
                }
            } 
            else
            { 
                switch (State) 
                {
                    case EntityState.Added: 
                        if (doFixup)
                        {
                            FixupRelationships();
                        } 
                        if (State != EntityState.Detached) //There is a possibility that as part of FixupRelationship entity moved to Detached state.
                        { 
                            Debug.Assert(EntityState.Added == State, "Expected ObjectStateEntry state is Added; make sure FixupRelationship did not corrupt cache entry state"); 
                            if (IsRelationship)
                            { 
                                // Remove key entry if necessary
                                DeleteUnnecessaryKeyEntries();
                                // Remove relationship entry
                                // devnote: Using this method instead of just changing the state because the entry 
                                //          may have already been detached along with the key entry above. However,
                                //          if there were other relationships using the key, it would not have been deleted. 
                                DetachRelationshipEntry(); 
                            }
                            else 
                            {
                                _cache.ChangeState(this, EntityState.Added, EntityState.Detached);
                            }
                            Debug.Assert(null == _modifiedFields, "There should not be any modified fields"); 
                        }
                        break; 
                    case EntityState.Modified: 
                        if (doFixup)
                        { 
                            FixupRelationships();
                        }
                        if (State != EntityState.Deleted) //There is a possibility that as part of FixupRelationship entity moved to Deleted state.
                        { 
                            Debug.Assert(EntityState.Modified == State, "Expected ObjectStateEntry state is Modified; make sure FixupRelationship did not corrupt cache entry state");
                            _cache.ChangeState(this, EntityState.Modified, EntityState.Deleted); 
                            State = EntityState.Deleted; 
                        }
                        break; 
                    case EntityState.Unchanged:
                        if (doFixup)
                        {
                            FixupRelationships(); 
                        }
                        else if (!IsRelationship) 
                        { 
                            // Even when we are not doing relationship fixup at the collection level, if the entry is not a relationship
                            // we need to check to see if there are relationships that are referencing keys that should be removed 
                            // this mainly occurs in cascade delete scenarios
                            DeleteRelationshipsThatReferenceKeys(null, null);
                        }
                        if (State != EntityState.Deleted)  //There is a possibility that as part of FixupRelationship entity moved to Deleted state. 
                        {
                            Debug.Assert(EntityState.Unchanged == State, "Expected ObjectStateEntry state is Unchanged; make sure FixupRelationship did not corrupt cache entry state"); 
                            _cache.ChangeState(this, EntityState.Unchanged, EntityState.Deleted); 
                            Debug.Assert(null == _modifiedFields, "There should not be any modified fields");
                            State = EntityState.Deleted; 
                        }
                        break;
                    //case DataRowState.Deleted:  no-op
                } 
            }
        } 
 
        private void FixupRelationships()
        { 
            RelationshipManager relationshipManager = EntityUtil.GetRelationshipManager(_entity);
            if (relationshipManager != null)
            {
                relationshipManager.RemoveEntityFromRelationships(); 
                DeleteRelationshipsThatReferenceKeys(null, null);
            } 
        } 

        ///  
        /// see if there are any relationship entries that point to key entries
        /// if there are, remove the relationship entry
        /// This is called when one of the ends of a relationship is being removed
        ///  
        /// An option relationshipSet; deletes only relationships that are part of this set
        internal void DeleteRelationshipsThatReferenceKeys(RelationshipSet relationshipSet, RelationshipEndMember endMember) 
        { 
            if (State != EntityState.Detached)
            { 
                // devnote: Need to use a copy of the relationships list because we may be deleting Added
                //          relationships, which will be removed from the list while we are still iterating
                foreach (ObjectStateEntry relationshipEntry in _cache.CopyOfRelationshipsByKey(EntityKey))
                { 
                    // Only delete the relationship entry if it is not already deleted (in which case we cannot access its values)
                    // and when the given (optionally) relationshipSet matches the one in teh relationship entry 
                    if ((relationshipEntry.State != EntityState.Deleted) && 
                        (relationshipSet == null || relationshipSet == relationshipEntry.EntitySet))
                    { 
                        ObjectStateEntry otherEnd = this.GetOtherEndOfRelationship(relationshipEntry);
                        if (endMember == null || endMember == otherEnd.GetAssociationEndMember(relationshipEntry))
                        {
                            for (int i = 0; i < 2; i++) 
                            {
                                EntityKey entityKey = relationshipEntry.GetCurrentRelationValue(i, false) as EntityKey; 
                                if ((object)entityKey != null) 
                                {
                                    ObjectStateEntry relatedEntry = _cache.GetObjectStateEntry(entityKey); 
                                    if (relatedEntry.IsKeyEntry)
                                    {
                                        // remove the relationshipEntry
                                        relationshipEntry.Delete(false); 
                                        break;
                                    } 
                                } 
                            }
                        } 
                    }
                }
            }
        } 

        ///  
        /// Detaches an entry and create in its place key entry if necessary 
        /// Removes relationships with another key entries and removes these key entries if necessary
        ///  
        internal void Detach()
        {
            ValidateState();
 
            Debug.Assert(!IsRelationship);
            Debug.Assert(!IsKeyEntry); 
 
            bool createKeyEntry = false;
 
            RelationshipManager relationshipManager = EntityUtil.GetRelationshipManager(_entity);
            if (relationshipManager != null)
            {
                // Key entry should be created only when current entity is not in Added state 
                // and if the entity is a "OneToOne" or "ZeroToOne" end of some existing relationship.
                createKeyEntry = 
                        this.State != EntityState.Added && 
                        this.IsOneEndOfSomeRelationship();
 
                // Remove current entity from collections/references (on both ends of relationship)
                // Relationship entries are removed from ObjectStateManager if current entity is in Added state
                // or if current entity is a "Many" end of the relationship.
                // NOTE In this step only relationship entries which have normal entity on the other end 
                //      can be removed.
                relationshipManager.DetachEntityFromRelationships(this.State); 
 
                // Remove relationship entries which has a key entry on the other end.
                // If the key entry does not have any other relationship, it is removed from Object State Manager. 
                // NOTE Relationship entries which have a normal entity on the other end are not modified in this step -
                //      they were modified in the previous step.
                this.DetachRelationshipsEntries(relationshipManager);
 
                if (createKeyEntry)
                { 
                    this.DegradeEntry(); 
                }
            } 

            if (!createKeyEntry)
            {
                // If entity is in state different than Added state, entityKey should not be set to null 
                // EntityKey is set to null in
                //    ObjectStateManger.ChangeState() -> 
                //    ObjectStateEntry.Reset() -> 
                //    ObjectStateEntry.DetachObjectStateManagerFromEntity()
 
                // Store data required to restore the entity key if needed.
                EntityKey key = _entityKey;
                EntityState state = State;
                object entity = _entity; 

                _cache.ChangeState(this, this.State, EntityState.Detached); 
 
                // In case the detach event modifies the key.
                if (state != EntityState.Added) 
                {
                    EntityUtil.SetKeyOntoEntity(entity, key);
                }
            } 
        }
 
        private bool IsOneEndOfSomeRelationship() 
        {
            foreach (ObjectStateEntry relationshipEntry in _cache.FindRelationshipsByKey(EntityKey)) 
            {
                RelationshipMultiplicity multiplicity = this.GetAssociationEndMember(relationshipEntry).RelationshipMultiplicity;
                if (multiplicity == RelationshipMultiplicity.One ||
                    multiplicity == RelationshipMultiplicity.ZeroOrOne) 
                {
                    return true; 
                } 
            }
            return false; 
        }

        internal void DetachRelationshipEntry()
        { 
            // no-op if already detached
            if (_cache != null) 
            { 
                Debug.Assert(IsRelationship, "this should be relationship entry");
                _cache.ChangeState(this, this.State, EntityState.Detached); 
            }
        }

        internal void RevertDelete() 
        {            // just change the state from deleted, to last state.
            if (!IsRelationship) 
            { 
                State = (_modifiedFields == null) ? EntityState.Unchanged : EntityState.Modified;
            } 
            else
            {
                State = EntityState.Unchanged;
            } 
            _cache.ChangeState(this, EntityState.Deleted, State);
        } 
 
        internal void Reset()
        { 
            DetachObjectStateManagerFromEntity();
            _cache = null;
            _entity = null;
            _entityKey = null; 
            _entitySet = null;
            _modifiedFields = null; 
            _originalValues = null; 
            _wrapper = null;
            State = EntityState.Detached; 
        }

        private static void ValidateRelationshipRange(int ordinal)
        { 
            if (unchecked(1u < (uint)ordinal))
            { 
                throw EntityUtil.ArgumentOutOfRange("ordinal"); 
            }
        } 

        private void ValidateState()
        {
            if (State == EntityState.Detached) 
            {
                throw EntityUtil.ObjectStateEntryinInvalidState(); 
            } 
            Debug.Assert(null != _cache, "null ObjectStateManager");
            Debug.Assert(null != _entitySet, "null EntitySetBase"); 
        }

        /// 
        /// API to return properties that are marked modified 
        /// 
        ///   
        ///  IEnumerable of modified properties names, names are in term of c-space  
        public IEnumerable GetModifiedProperties()
        { 
            ValidateState();
            if (EntityState.Modified == _state && _modifiedFields != null)
            {
                Debug.Assert(null != _modifiedFields, "null fields"); 
                for (int i = 0; i < _modifiedFields.Count; i++)
                { 
                    if (_modifiedFields[i]) 
                    {
                        yield return (IsRelationship ? GetCLayerName(i, null) : GetCLayerName(i, _cacheTypeMetadata)); 
                    }
                }
            }
        } 

        #region IEntityChangeTracker 
 
        /// 
        /// Used to report that a scalar entity property is about to change 
        /// The current value of the specified property is cached when this method is called.
        /// 
        /// The name of the entity property that is changing
        void IEntityChangeTracker.EntityMemberChanging(string entityMemberName) 
        {
            EntityValueChanging(entityMemberName, null, null); 
        } 

        ///  
        /// Used to report that a scalar entity property has been changed
        /// The property value that was cached during EntityMemberChanging is now
        /// added to OriginalValues
        ///  
        /// The name of the entity property that has changing
        void IEntityChangeTracker.EntityMemberChanged(string entityMemberName) 
        { 
            EntityValueChanged(entityMemberName, null, null);
        } 

        /// 
        /// Used to report that a complex property is about to change
        /// The current value of the specified property is cached when this method is called. 
        /// 
        /// The name of the top-level entity property that is changing 
        /// The complex object that contains the property that is changing 
        /// The name of the property that is changing on complexObject
        void IEntityChangeTracker.EntityComplexMemberChanging(string entityMemberName, object complexObject, string complexObjectMemberName) 
        {
            EntityUtil.CheckArgumentNull(complexObjectMemberName, "complexObjectMemberName");
            EntityUtil.CheckArgumentNull(complexObject, "complexObject");
            EntityValueChanging(entityMemberName, complexObject, complexObjectMemberName); 
        }
 
        ///  
        /// Used to report that a complex property has been changed
        /// The property value that was cached during EntityMemberChanging is now added to OriginalValues 
        /// 
        /// The name of the top-level entity property that has changed
        /// The complex object that contains the property that changed
        /// The name of the property that changed on complexObject 
        void IEntityChangeTracker.EntityComplexMemberChanged(string entityMemberName, object complexObject, string complexObjectMemberName)
        { 
            EntityUtil.CheckArgumentNull(complexObjectMemberName, "complexObjectMemberName"); 
            EntityUtil.CheckArgumentNull(complexObject, "complexObject");
            EntityValueChanged(entityMemberName, complexObject, complexObjectMemberName); 
        }

        /// 
        /// Returns the EntityState from the ObjectStateEntry 
        /// 
        EntityState IEntityChangeTracker.EntityState 
        { 
            get
            { 
                return this.State;
            }
        }
 
        #endregion
 
        #region Private Change Tracking Methods 

        ///  
        /// Method called to start the change tracking process on an entity property. The current property value is cached at
        /// this stage in preparation for later storage in the original values record. Multiple successful calls to this method
        /// will overwrite the cached values.
        /// All inputs are in OSpace. 
        /// 
        /// Name of the top-level entity property that is changing 
        /// If entityMemberName refers to a complex property, this is the complex 
        /// object that contains the change. Otherwise this is null.
        /// If entityMemberName refers to a complex property, this is the name of 
        /// the property that is changing on complexObject. Otherwise this is null.
        private void EntityValueChanging(string entityMemberName, object complexObject, string complexObjectMemberName)
        {
            string changingMemberName; 
            StateManagerTypeMetadata typeMetadata;
            object changingObject; 
 
            // Get the metadata for the property that is changing, and verify that it is valid to change it for this entry
            int changingOrdinal = GetAndValidateChangeMemberInfo(entityMemberName, complexObject, complexObjectMemberName, 
                out typeMetadata, out changingMemberName, out changingObject);

            // if EntityKey is changing and is in a valid scenario for it to change, no further action is needed
            if (changingOrdinal == -2) 
            {
                return; 
            } 

            Debug.Assert(changingOrdinal != -1, "Expected GetAndValidateChangeMemberInfo to throw for a invalid property name"); 

            // Cache the current value for later storage in original values. If we are not in a state where we should update
            // the original values, we don't even need to bother saving the current value here. However, we will still cache
            // the other data regarding the change, so that we always require matching Changing and Changed calls, regardless of the state. 
            StateManagerMemberMetadata memberMetadata = typeMetadata.Member(changingOrdinal);
            object oldValue = null; 
            if (_state == EntityState.Unchanged || 
                (_state == EntityState.Modified && !FindOriginalValue(memberMetadata, changingObject)))
            { 
                // devnote: Not using GetCurrentEntityValue here because change tracking can only be done on OSpace members,
                //          so we don't need to worry about shadow state, and we don't want a CSpace representation of complex objects

                oldValue = memberMetadata.GetValue(changingObject); 
                _cache.SaveOriginalValues = true;
            } 
            else 
            {
                // we are not in a state where the values will be saved 
                _cache.SaveOriginalValues = false;
            }
            Debug.Assert(this.State != EntityState.Detached, "Change tracking should not happen on detached entities.");
            SetCachedChangingValues(entityMemberName, changingObject, changingMemberName, this.State, oldValue); 
        }
 
        ///  
        /// Method called to complete the change tracking process on an entity property. The original property value
        /// is now saved in the original values record if there is not already an entry in the record for this property. 
        /// The parameters to this method must have the same values as the parameter values passed to the last call to
        /// EntityValueChanging on this ObjectStateEntry.
        /// All inputs are in OSpace.
        ///  
        /// Name of the top-level entity property that has changed
        /// If entityMemberName refers to a complex property, this is the complex 
        /// object that contains the change. Otherwise this is null. 
        /// If entityMemberName refers to a complex property, this is the name of
        /// the property that has changed on complexObject. Otherwise this is null. 
        private void EntityValueChanged(string entityMemberName, object complexObject, string complexObjectMemberName)
        {
            string changingMemberName;
            StateManagerTypeMetadata typeMetadata; 
            object changingObject;
 
            // Get the metadata for the property that is changing, and verify that it is valid to change it for this entry 
            // If something fails, we will clear out our cached values in the finally block, and require the user to submit another Changing notification
            try 
            {
                int changingOrdinal = GetAndValidateChangeMemberInfo(entityMemberName, complexObject, complexObjectMemberName,
                    out typeMetadata, out changingMemberName, out changingObject);
 
                // if EntityKey is changing and is in a valid scenario for it to change, no further action is needed
                if (changingOrdinal == -2) 
                { 
                    return;
                } 

                // Verify that the inputs to this call match the values we have cached
                if ((changingObject != _cache.ChangingObject) ||
                    (changingMemberName != _cache.ChangingMember) || 
                    (entityMemberName != _cache.ChangingEntityMember))
                { 
                    throw EntityUtil.EntityValueChangedWithoutEntityValueChanging(); 
                }
 
                // check the state after the other values because if the other cached values have not been set and are null, it is more
                // intuitive to the user to get an error that specifically points to that as the problem, and in that case, the state will
                // also not be matched, so if we checked this first, it would cause a confusing error to be thrown.
                if (this.State != _cache.ChangingState) 
                {
                    throw EntityUtil.ChangedInDifferentStateFromChanging(this.State, _cache.ChangingState); 
                } 

                if (_cache.SaveOriginalValues) 
                {
                    StateManagerMemberMetadata memberMetadata = typeMetadata.Member(changingOrdinal);
                    object oldValue = _cache.ChangingOldValue;
 
                    // Expand only non-null complex type values
                    if (memberMetadata.IsComplex && oldValue != null) 
                    { 
                        // devnote: Not using GetCurrentEntityValue here because change tracking can only be done on OSpace members,
                        //          so we don't need to worry about shadow state, and we don't want a CSpace representation of complex objects 
                        object newValue = memberMetadata.GetValue(changingObject);
                        ExpandComplexTypeAndAddValues(memberMetadata, oldValue, newValue, false);
                    }
                    else 
                    {
                        AddOriginalValue(memberMetadata, changingObject, oldValue); 
                    } 
                }
 
                EntityState initialState = State;
                if (State != EntityState.Added)
                {
                    State = EntityState.Modified; 
                }
                if (State == EntityState.Modified) 
                { 
                    SetModifiedProperty(entityMemberName);
                } 
                if (initialState != _state)
                {
                    _cache.ChangeState(this, initialState, _state);
                } 
            }
            finally 
            { 
                SetCachedChangingValues(null, null, null, EntityState.Detached, null);
            } 
        }


 
        /// 
        /// Helper method to validate that the property names being reported as changing/changed are valid for this entity and that 
        /// the entity is in a valid state for the change request. Also determines if this is a change on a complex object, and 
        /// returns the appropriate metadata and object to be used for the rest of the changing and changed operations.
        ///  
        /// Top-level entity property name
        /// Complex object that contains the change, null if the change is on a top-level entity property
        /// Name of the property that is changing on the complexObject, null for top-level entity properties
        /// Metadata for the type that contains the change, either for the entity itself or for the complex object 
        /// Property name that is actually changing -- either entityMemberName for entities or
        /// complexObjectMemberName for complex objects 
        /// Object reference that contains the change, either the entity or complex object 
        /// as appropriate for the requested change
        /// Ordinal of the property that is changing, or -2 if the EntityKey is changing in a valid scenario. This is relative 
        /// to the returned typeMetadata. Throws exceptions if the requested property name(s) are invalid for this entity.
        private int GetAndValidateChangeMemberInfo(string entityMemberName, object complexObject, string complexObjectMemberName,
            out StateManagerTypeMetadata typeMetadata, out string changingMemberName, out object changingObject)
        { 
            typeMetadata = null;
            changingMemberName = null; 
            changingObject = null; 

            Debug.Assert(!IsRelationship, "Change tracking methods should not be called on relationship entries."); 

            EntityUtil.CheckArgumentNull(entityMemberName, "entityMemberName");
            // complexObject and complexObjectMemberName are allowed to be null here for change tracking on top-level entity properties
 
            ValidateState();
 
            int changingOrdinal = _cacheTypeMetadata.GetOrdinalforOLayerMemberName(entityMemberName); 
            if (changingOrdinal == -1)
            { 
                if (entityMemberName == StructuralObject.EntityKeyPropertyName)
                {
                    // Setting EntityKey property is only allowed from here when we are in the middle of relationship fixup.
                    if (!_cache.InRelationshipFixup) 
                    {
                        throw EntityUtil.CantSetEntityKey(); 
                    } 
                    else
                    { 
                        // If we are in fixup, there is nothing more to do here with EntityKey, so just
                        // clear the saved changing values and return. This will ensure that we behave
                        // the same with the change notifications on EntityKey as with other properties.
                        // I.e. we still don't allow the following: 
                        //     EntityMemberChanging("Property1")
                        //     EntityMemberChanging("EntityKey") 
                        //     EntityMemberChanged("EntityKey") 
                        //     EntityMemberChanged("Property1")
                        Debug.Assert(this.State != EntityState.Detached, "Change tracking should not happen on detached entities."); 
                        SetCachedChangingValues(null, null, null, this.State, null);
                        return -2;
                    }
                } 
                else
                { 
                    throw EntityUtil.ChangeOnUnmappedProperty(entityMemberName); 
                }
            } 
            else
            {
                StateManagerTypeMetadata tmpTypeMetadata;
                string tmpChangingMemberName; 
                object tmpChangingObject;
 
                // entityMemberName is a confirmed valid property on the Entity, but if this is a complex type we also need to validate its property 
                if (complexObject != null)
                { 
                    // a complex object was provided, but the top-level Entity property is not complex
                    if (!_cacheTypeMetadata.Member(changingOrdinal).IsComplex)
                    {
                        throw EntityUtil.ComplexChangeRequestedOnScalarProperty(entityMemberName); 
                    }
 
                    tmpTypeMetadata = _cache.GetOrAddStateManagerTypeMetadata(complexObject.GetType(), (EntitySet)this.EntitySet); 
                    changingOrdinal = tmpTypeMetadata.GetOrdinalforOLayerMemberName(complexObjectMemberName);
                    if (changingOrdinal == -1) 
                    {
                        throw EntityUtil.ChangeOnUnmappedComplexProperty(complexObjectMemberName);
                    }
 
                    tmpChangingMemberName = complexObjectMemberName;
                    tmpChangingObject = complexObject; 
                } 
                else
                { 
                    tmpTypeMetadata = _cacheTypeMetadata;
                    tmpChangingMemberName = entityMemberName;
                    tmpChangingObject = this.Entity;
                } 

                VerifyEntityValueIsEditable(tmpTypeMetadata, changingOrdinal, tmpChangingMemberName); 
 
                typeMetadata = tmpTypeMetadata;
                changingMemberName = tmpChangingMemberName; 
                changingObject = tmpChangingObject;
                return changingOrdinal;
            }
        } 

        ///  
        /// Helper method to set the information needed for the change tracking cache. Ensures that all of these values get set together. 
        /// 
        private void SetCachedChangingValues(string entityMemberName, object changingObject, string changingMember, EntityState changingState, object oldValue) 
        {
            _cache.ChangingEntityMember = entityMemberName;
            _cache.ChangingObject = changingObject;
            _cache.ChangingMember = changingMember; 
            _cache.ChangingState = changingState;
            _cache.ChangingOldValue = oldValue; 
            if (changingState == EntityState.Detached) 
            {
                _cache.SaveOriginalValues = false; 
            }
        }

        ///  
        /// Helper method to recursively expand a complex object's values down to scalars for storage in the original values record.
        /// This method is used when a whole complex object is set on its parent object, instead of just setting 
        /// individual scalar values on that object. 
        /// 
        /// metadata for the complex property being expanded on the parent 
        /// where the parent can be an entity or another complex object
        /// Old value of the complex property. Scalar values from this object are stored in the original values record
        /// New value of the complex property. This object reference is used in the original value record and is
        /// associated with the scalar values for the same property on the oldComplexObject 
        /// Whether or not to use the existing complex object in the original values or to use the original value that is already present 
        private void ExpandComplexTypeAndAddValues(StateManagerMemberMetadata memberMetadata, object oldComplexObject, object newComplexObject, bool useOldComplexObject) 
        { 
            Debug.Assert(newComplexObject != null, "null new object(s) cannot be used in complex object expansion");
            Debug.Assert(memberMetadata.IsComplex, "Cannot expand non-complex objects"); 
            Debug.Assert(oldComplexObject == null || (oldComplexObject.GetType() == newComplexObject.GetType()), "Cannot replace a complex object with an object of a different type, unless the original one was null");

            StateManagerTypeMetadata typeMetadata = _cache.GetOrAddStateManagerTypeMetadata(memberMetadata.CdmMetadata.TypeUsage.EdmType);
            object retValue; 
            for (int field = 0; field < typeMetadata.FieldCount; field++)
            { 
                StateManagerMemberMetadata complexMemberMetadata = typeMetadata.Member(field); 
                if (complexMemberMetadata.IsComplex)
                { 
                    object oldComplexMemberValue = null;
                    if (oldComplexObject != null)
                    {
                        oldComplexMemberValue = complexMemberMetadata.GetValue(oldComplexObject); 
                        if (oldComplexMemberValue == null && FindOriginalValue(complexMemberMetadata, oldComplexObject, out retValue))
                        { 
                            _originalValues.Remove((StateManagerValue)retValue); 
                        }
                    } 
                    ExpandComplexTypeAndAddValues(complexMemberMetadata, oldComplexMemberValue, complexMemberMetadata.GetValue(newComplexObject), useOldComplexObject);
                }
                else
                { 
                    object originalValue = null;
                    object complexObject = newComplexObject; 
 
                    if (useOldComplexObject)
                    { 
                        // Set the original values using the existing current value object
                        // complexObject --> the existing complex object
                        // originalValue --> the new value to set for this member
                        originalValue = complexMemberMetadata.GetValue(newComplexObject); 
                        complexObject = oldComplexObject;
                    } 
                    else 
                    {
                        if (oldComplexObject != null) 
                        {
                            // If we already have an entry for this property in the original values list, we need to remove it. We can't just
                            // update it because StateManagerValue is a struct and there is no way to get a reference to the entry in the list.
                            originalValue = complexMemberMetadata.GetValue(oldComplexObject); 
                            if (FindOriginalValue(complexMemberMetadata, oldComplexObject, out retValue))
                            { 
                                StateManagerValue originalStateValue = ((StateManagerValue)retValue); 
                                _originalValues.Remove(originalStateValue);
                                originalValue = originalStateValue.originalValue; 
                            }
                        }
                        else
                        { 
                            originalValue = complexMemberMetadata.GetValue(newComplexObject);
                        } 
                    } 

 
                    // Add the new entry. The userObject will reference the new complex object that is currently being set.
                    // If the value was in the list previously, we will still use the old value with the new object reference.
                    // That will ensure that we preserve the old value while still maintaining the link to the
                    // existing complex object that is attached to the entity or parent complex object. If an entry is already 
                    // in the list this means that it was either explicitly set by the user or the entire complex type was previously
                    // set and expanded down to the individual properties.  In either case we do the same thing. 
                    AddOriginalValue(complexMemberMetadata, complexObject, originalValue); 
                }
            } 
        }

        #endregion
 
        /// 
        /// Verifies that the property with the given ordinal is editable. 
        ///  
        /// the property is not editable
        internal void VerifyEntityValueIsEditable(StateManagerTypeMetadata typeMetadata, int ordinal, string memberName) 
        {
            if (_state == EntityState.Deleted)
            {
                throw EntityUtil.CantModifyDetachedDeletedEntries(); 
            }
 
            Debug.Assert(typeMetadata != null, "Cannot verify entity or complex object is editable if typeMetadata is null."); 
            StateManagerMemberMetadata member = typeMetadata.Member(ordinal);
 
            Debug.Assert(member != null, "Member shouldn't be null.");

            // Key fields are only editable if the entry is the Added state.
            if (member.IsPartOfKey && State != EntityState.Added) 
            {
                throw EntityUtil.CannotModifyKeyProperty(memberName); 
            } 
        }
 
        /// 
        /// set the state to Modified.
        /// 
        ///  
        /// 
        /// If State is not Modified or Unchanged 
        /// 
        public void SetModified()
        { 
            ValidateState();

            if (IsRelationship)
            { 
                throw EntityUtil.CantModifyRelationState();
            } 
            else if (IsKeyEntry) 
            {
                throw EntityUtil.CannotModifyKeyEntryState(); 
            }
            else
            {
                if (EntityState.Unchanged == State) 
                {
                    State = EntityState.Modified; 
                    _cache.ChangeState(this, EntityState.Unchanged, State); 
                }
                else if (EntityState.Modified != State) 
                {
                    throw EntityUtil.SetModifiedStates();
                }
            } 
        }
 
        ///  
        /// Marks specified property as modified.
        ///  
        /// This API recognizes the names in terms of OSpace
        /// If State is not Modified or Unchanged
        ///
        public void SetModifiedProperty(string propertyName) 
        {
            ValidateState(); 
            if (IsRelationship) 
            {
                throw EntityUtil.CantModifyRelationState(); 
            }
            else if (IsKeyEntry)
            {
                throw EntityUtil.CannotModifyKeyEntryState(); 
            }
            else 
            { 
                if (EntityState.Unchanged == State)
                { 
                    State = EntityState.Modified;
                    _cache.ChangeState(this, EntityState.Unchanged, State);
                }
                if (EntityState.Modified == State) 
                {
                    if (null == _modifiedFields) 
                    { 
                        _modifiedFields = new BitArray(GetFieldCount(_cacheTypeMetadata));
                    } 

                    EntityUtil.CheckArgumentNull(propertyName, "propertyName");
                    int ordinal = _cacheTypeMetadata.GetOrdinalforOLayerMemberName(propertyName);
                    if (ordinal == -1) 
                    {
                        throw EntityUtil.InvalidModifiedPropertyName(propertyName); 
                    } 
                    _modifiedFields.Set(ordinal, true);
                } 
                else
                {
                    throw EntityUtil.SetModifiedStates();
                } 
            }
        } 
 
        internal void SetModifiedAll()
        { 
            ValidateState();
            if (!IsRelationship)
            {
                Debug.Assert(State == EntityState.Modified, "SetModifiedAll called when not modified"); 
                if (null == _modifiedFields)
                { 
                    _modifiedFields = new BitArray(GetFieldCount(_cacheTypeMetadata)); 
                }
                _modifiedFields.SetAll(true); 
            }
            else
            {
                throw EntityUtil.CantModifyRelationState(); 
            }
        } 
 
        /// 
        /// Reuse or create a new (Entity)DataRecordInfo. 
        /// 
        internal DataRecordInfo GetDataRecordInfo(StateManagerTypeMetadata metadata, object userObject)
        {
            if (IsRelationship) 
            {
                //Dev Note: RelationshipType always has default facets. Thus its safe to construct a TypeUsage from EdmType 
                return new DataRecordInfo(TypeUsage.Create(((RelationshipSet)EntitySet).ElementType)); 
            }
            else if (Helper.IsEntityType(metadata.CdmMetadata.EdmType) && (null != (object)_entityKey)) 
            {
                // is EntityType with null EntityKey when constructing new EntityKey during ObjectStateManager.Add
                // always need a new EntityRecordInfo instance for the different key (reusing DataRecordInfo's FieldMetadata).
                return new EntityRecordInfo(metadata.DataRecordInfo, _entityKey, (EntitySet)EntitySet); 
            }
            else 
            { 
                // ObjectContext.AttachTo uses CurrentValueRecord to build EntityKey for EntityType
                // so the Entity doesn't have an EntityKey yet, SQLBU 525130 
                //Debug.Assert(Helper.IsComplexType(metadata.CdmMetadata.EdmType), "!IsComplexType");
                return metadata.DataRecordInfo;
            }
        } 

        // This API are mainly for DbDataRecord implementations to get and set the values 
        // also for loadoptions, setoldvalue will be used. 
        // we should handle just for C-space, we will not recieve a call from O-space for set
        // We will not also return any value in term of O-Layer. all set and gets for us is in terms of C-layer. 
        // the only O-layer interaction we have is through delegates from entity.
        internal void SetCurrentEntityValue(StateManagerTypeMetadata metadata, int ordinal, object userObject, object newValue)
        {
            // required to validate state because entity could be detatched from this context and added to another context 
            // and we want this to fail instead of setting the value which would redirect to the other context
            ValidateState(); 
 
            StateManagerMemberMetadata member = metadata.Member(ordinal);
            Debug.Assert(member != null, "StateManagerMemberMetadata was not found for the given ordinal."); 

            if (member.IsComplex)
            {
                if (newValue == null) 
                {
                    throw EntityUtil.NullableComplexTypesNotSupported(member.CLayerName); 
                } 

                IExtendedDataRecord newValueRecord = newValue as IExtendedDataRecord; 
                if (newValueRecord == null)
                {
                    throw EntityUtil.InvalidComplexDataRecordObject(member.CLayerName);
                } 

                newValue = _cache.ComplexTypeMaterializer.CreateComplex(newValueRecord, newValueRecord.DataRecordInfo, null); 
            } 

            member.SetValue(userObject, newValue); 
        }

        // helper method used to set value of property
        internal void SetCurrentEntityValue(string memberName, object newValue) 
        {
            int ordinal = _cacheTypeMetadata.GetOrdinalforOLayerMemberName(memberName); 
            SetCurrentEntityValue(_cacheTypeMetadata, ordinal, _entity, newValue); 
        }
 
        internal void SetOriginalEntityValue(StateManagerTypeMetadata metadata, int ordinal, object userObject, object newValue)
        {
            ValidateState();
            if (State == EntityState.Added) 
            {
                throw EntityUtil.OriginalValuesDoesNotExist(); 
            } 

            EntityState initialState = State; 

            object orgValue; // StateManagerValue
            object oldOriginalValue; // the actual value
 
            // Update original values list
            StateManagerMemberMetadata memberMetadata = metadata.Member(ordinal); 
            if (FindOriginalValue(memberMetadata, userObject, out orgValue)) 
            {
                _originalValues.Remove((StateManagerValue)orgValue); 
            }

            if (memberMetadata.IsComplex)
            { 
                oldOriginalValue = memberMetadata.GetValue(userObject);
                if (oldOriginalValue == null) 
                { 
                    throw EntityUtil.NullableComplexTypesNotSupported(memberMetadata.CLayerName);
                } 

                IExtendedDataRecord newValueRecord = newValue as IExtendedDataRecord;
                if (newValueRecord != null)
                { 
                    // Requires materialization
                    newValue = _cache.ComplexTypeMaterializer.CreateComplex(newValueRecord, newValueRecord.DataRecordInfo, null); 
                } 

                // We only store scalar properties values in original values, so no need to search the list 
                // if the property being set is complex. Just get the value as an OSpace object.
                ExpandComplexTypeAndAddValues(memberMetadata, oldOriginalValue, newValue, true);
            }
            else 
            {
                AddOriginalValue(memberMetadata, userObject, newValue); 
            } 

            if (initialState == EntityState.Unchanged) 
            {
                State = EntityState.Modified;
            }
        } 

        private bool FindOriginalValue(StateManagerMemberMetadata metadata, object instance) 
        { 
            object tmp;
            return FindOriginalValue(metadata, instance, out tmp); 
        }
        private bool FindOriginalValue(StateManagerMemberMetadata metadata, object instance, out object value)
        {
            bool found = false; 
            object retValue = null;
            if (null != _originalValues) 
            { 
                foreach (StateManagerValue cachevalue in _originalValues)   // this should include also shadow state
                { 
                    if (cachevalue.userObject == instance && cachevalue.memberMetadata == metadata)
                    {
                        found = true;
                        retValue = cachevalue; 
                        break;
                    } 
                } 
            }
            value = retValue; 
            return found;
        }

        // Get AssociationEndMember of current entry of given relationship 
        // Relationship must be related to the current entry.
        internal AssociationEndMember GetAssociationEndMember(ObjectStateEntry relationshipEntry) 
        { 
            Debug.Assert(relationshipEntry.IsRelationship, "entry should be a relationship entry");
            Debug.Assert((object)this.EntityKey != null, "entry should have a not null EntityKey"); 

            ValidateState();

            AssociationEndMember endMember = relationshipEntry.Wrapper.GetAssociationEndMember(EntityKey); 
            Debug.Assert(null != endMember, "should be one of the ends of the relationship");
            return endMember; 
        } 

        // Get entry which is on the other end of given relationship. 
        // Relationship must be related to the current entry.
        internal ObjectStateEntry GetOtherEndOfRelationship(ObjectStateEntry relationshipEntry)
        {
            Debug.Assert(relationshipEntry.IsRelationship, "entry should be a relationship entry"); 
            Debug.Assert((object)this.EntityKey != null, "entry should have a not null EntityKey");
 
            return _cache.GetObjectStateEntry(relationshipEntry.Wrapper.GetOtherEntityKey(this.EntityKey)); 
        }
 
        // Helper method to determine if the specified entityKey is in the given role and AssociationSet in this relationship entry
        // Assumes that the entityKey is on one side of the relationship.
        internal bool IsSameAssociationSetAndRole(AssociationSet associationSet, EntityKey entityKey, AssociationEndMember associationMember)
        { 
            return Object.ReferenceEquals(_entitySet, associationSet)
                && (_wrapper.GetAssociationEndMember(entityKey).Name == associationMember.Name); 
        } 

        internal object GetCurrentRelationValue(int ordinal) 
        {
            return GetCurrentRelationValue(ordinal, true);
        }
 
        private object GetCurrentRelationValue(int ordinal, bool throwException)
        { 
            ValidateRelationshipRange(ordinal); 
            ValidateState();
            if (State == EntityState.Deleted && throwException) 
            {
                throw EntityUtil.CurrentValuesDoesNotExist();
            }
            return _wrapper.GetEntityKey(ordinal); 
        }
 
        internal object GetCurrentEntityValue(StateManagerTypeMetadata metadata, int ordinal, object userObject, ObjectStateValueRecord updatableRecord) 
        {
            ValidateState(); 

            object retValue = null;
            StateManagerMemberMetadata member = metadata.Member(ordinal);
            Debug.Assert(null != member, "didn't throw ArgumentOutOfRangeException"); 

            if (!metadata.IsMemberPartofShadowState(ordinal)) 
            { // if it is not shadow state 
                retValue = member.GetValue(userObject);
 
                // Wrap the value in a record if it is a non-null complex type
                if (member.IsComplex && retValue != null)
                {
                    // need to get the new StateManagerTypeMetadata for nested /complext member 
                    switch (updatableRecord)
                    { 
                        case ObjectStateValueRecord.OriginalReadonly: 
                            retValue = new ObjectStateEntryDbDataRecord(this,
                                _cache.GetOrAddStateManagerTypeMetadata(member.CdmMetadata.TypeUsage.EdmType), retValue); 
                            break;
                        case ObjectStateValueRecord.CurrentUpdatable:
                            retValue = new ObjectStateEntryDbUpdatableDataRecord(this,
                                _cache.GetOrAddStateManagerTypeMetadata(member.CdmMetadata.TypeUsage.EdmType), retValue); 
                            break;
                        case ObjectStateValueRecord.OriginalUpdatable: 
                            retValue = new ObjectStateEntryOriginalDbUpdatableDataRecord(this, 
                                _cache.GetOrAddStateManagerTypeMetadata(member.CdmMetadata.TypeUsage.EdmType), retValue);
                            break; 
                        default:
                            Debug.Assert(false, "shouldn't happen");
                            break;
                    } 
                    // we need to pass the toplevel ordinal
                } 
            } 
#if DEBUG // performance, don't do this work in retail until shadow state is supported
            else if (userObject == _entity) 
            {
                Debug.Assert(false, "shadowstate not supported");
#if SupportShadowState
                Debug.Assert(null != _currentValues, "shadow state without values"); 
                _currentValues.TryGetValue(member.CLayerName, out retValue); // try to get it from shadow state if exists
                // we don't support CSpace only complex type 
#endif 
            }
#endif 
            return retValue ?? DBNull.Value;
        }

        // helper method used to get value of property 
        internal object GetCurrentEntityValue(string memberName)
        { 
            int ordinal = _cacheTypeMetadata.GetOrdinalforOLayerMemberName(memberName); 
            return GetCurrentEntityValue(_cacheTypeMetadata, ordinal, _entity, ObjectStateValueRecord.CurrentUpdatable);
        } 

        internal object GetOriginalEntityValue(StateManagerTypeMetadata metadata, int ordinal, object userObject, ObjectStateValueRecord updatableRecord)
        {
            // if original value is stored, then use it, otherwise use the current value from the entity 
            ValidateState();
            object retValue; 
            StateManagerMemberMetadata member = metadata.Member(ordinal); 
            if (FindOriginalValue(member, userObject, out retValue))
            { 
                // If the object is null, return DBNull.Value to be consistent with GetCurrentEntityValue
                return ((StateManagerValue)retValue).originalValue ?? DBNull.Value;
            }
            return GetCurrentEntityValue(metadata, ordinal, userObject, updatableRecord); 
        }
 
        internal object GetOriginalRelationValue(int ordinal) 
        {
            return GetCurrentRelationValue(ordinal, false); 
        }

        // Get values of key properties which doesn't already exist in passed in 'properties'
        internal void GetOtherKeyProperties(Dictionary> properties) 
        {
            Debug.Assert(properties != null); 
            Debug.Assert(_cacheTypeMetadata != null); 
            Debug.Assert(_cacheTypeMetadata.DataRecordInfo != null);
            Debug.Assert(_cacheTypeMetadata.DataRecordInfo.RecordType != null); 

            EntityType entityType = _cacheTypeMetadata.DataRecordInfo.RecordType.EdmType as EntityType;
            Debug.Assert(entityType != null, "EntityType == null");
 
            foreach (EdmMember member in entityType.KeyMembers)
            { 
                if (!properties.ContainsKey(member.Name)) 
                {
                    properties[member.Name] = new KeyValuePair(this.GetCurrentEntityValue(member.Name), new IntBox(1)); 
                }
            }
        }
 
        private void AddOriginalValue(StateManagerMemberMetadata memberMetadata, object userObject, object value)
        { 
            if (null == _originalValues) 
            {
                _originalValues = new List(); 
            }
            _originalValues.Add(new StateManagerValue(memberMetadata, userObject, value));
        }
 
        internal void CompareKeyProperties(object changed)
        { 
            Debug.Assert(changed != null, "changed object is null"); 
            Debug.Assert(!this.IsKeyEntry);
            Debug.Assert(!this.IsRelationship); 

            StateManagerTypeMetadata metadata = this._cacheTypeMetadata;

            int fieldCount = this.GetFieldCount(metadata); 
            object currentValueNew;
            object currentValueOld; 
 
            for (int i = 0; i < fieldCount; i++)
            { 
                StateManagerMemberMetadata member = metadata.Member(i);
                if (member.IsPartOfKey)
                {
                    Debug.Assert(!member.IsComplex); 

                    currentValueNew = member.GetValue(changed); 
                    currentValueOld = member.GetValue(this._entity); 

                    if (!Object.Equals(currentValueNew, currentValueOld)) 
                    {
                        throw EntityUtil.CannotModifyKeyProperty(member.CLayerName);
                    }
                } 
            }
        } 
    } 

    internal struct StateManagerValue 
    {
        internal StateManagerMemberMetadata memberMetadata;
        internal object userObject;
        internal object originalValue; 

        internal StateManagerValue(StateManagerMemberMetadata metadata, object instance, object value) 
        { 
            memberMetadata = metadata;
            userObject = instance; 
            originalValue = value;
        }
    }
 
    internal enum ObjectStateValueRecord
    { 
        OriginalReadonly = 0, 
        CurrentUpdatable = 1,
        OriginalUpdatable = 2, 
    }


    // This class is used in Referential Integrity Constraints feature. 
    // It is used to get around the problem of enumerating dictionary contents,
    // but allowing update of the value without breaking the enumerator. 
    internal sealed class IntBox 
    {
        private int val; 

        internal IntBox(int val)
        {
            this.val = val; 
        }
 
        internal int Value 
        {
            get 
            {
                return val;
            }
 
            set
            { 
                val = value; 
            }
        } 
    }
}

 

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