RelationshipEntry.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataEntity / System / Data / Objects / RelationshipEntry.cs / 1305376 / RelationshipEntry.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner       [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 
using System;
using System.Collections.Generic; 
using System.Text;
using System.Diagnostics;
using System.Data.Objects.DataClasses;
using System.Data.Metadata.Edm; 
using System.Data.Objects.Internal;
using System.Data.Common; 
 
namespace System.Data.Objects
{ 
    internal sealed class RelationshipEntry : ObjectStateEntry
    {
#if DEBUG
        private static int _objectTypeCount; // Bid counter 
        internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
#endif 
 
        internal RelationshipWrapper _relationshipWrapper;
        internal EntityKey Key0 { get { return RelationshipWrapper.Key0; } } 
        internal EntityKey Key1 { get { return RelationshipWrapper.Key1; } }
        internal override System.Collections.BitArray ModifiedProperties
        {
            get { return null; } 
        }
 
        #region Linked list of related relationships 
        private RelationshipEntry _nextKey0;
        private RelationshipEntry _nextKey1; 
        #endregion

        #region Constructors
        internal RelationshipEntry(ObjectStateManager cache, EntityState state, RelationshipWrapper relationshipWrapper) 
            : base(cache, null, state)
        { 
            Debug.Assert(null != relationshipWrapper, "null RelationshipWrapper"); 
            Debug.Assert(EntityState.Added == state ||
                         EntityState.Unchanged == state || 
                         EntityState.Deleted == state,
                         "invalid EntityState");

            base._entitySet = relationshipWrapper.AssociationSet; 
            _relationshipWrapper = relationshipWrapper;
        } 
        #endregion 

        #region Public members 

        /// 
        /// API to accept the current values as original values and  mark the entity as Unchanged.
        ///  
        /// 
        ///  
        override public bool IsRelationship 
        {
            get 
            {
                ValidateState();
                return true;
            } 
        }
 
        override public void AcceptChanges() 
        {
            ValidateState(); 

            switch (State)
            {
                case EntityState.Deleted: 
                    DeleteUnnecessaryKeyEntries();
                    // 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:
                    _cache.ChangeState(this, EntityState.Added, EntityState.Unchanged); 
                    State = EntityState.Unchanged;
                    break; 
                case EntityState.Modified: 
                    Debug.Assert(false, "RelationshipEntry cannot be in Modified state");
                    break; 
                case EntityState.Unchanged:
                    break;
            }
        } 

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

        override public IEnumerable GetModifiedProperties() 
        { 
            ValidateState();
            yield break; 
        }

        override public void SetModified()
        { 
            ValidateState();
            throw EntityUtil.CantModifyRelationState(); 
        } 

        override public object Entity 
        {
            get
            {
                ValidateState(); 
                return null;
            } 
        } 

        override public EntityKey EntityKey 
        {
            get
            {
                ValidateState(); 
                return null;
            } 
            internal set 
            {
                // no-op for entires other than EntityEntry 
                Debug.Assert(false, "EntityKey setter shouldn't be called for RelationshipEntry");
            }
        }
 
        /// 
        /// Marks specified property as modified. 
        ///  
        /// This API recognizes the names in terms of OSpace
        /// If State is not Modified or Unchanged 
        ///
        override public void SetModifiedProperty(string propertyName)
        {
            ValidateState(); 

            throw EntityUtil.CantModifyRelationState(); 
        } 

        ///  
        /// Original values of entity
        /// 
        /// 
        ///  DbDataRecord  
        [DebuggerBrowsable(DebuggerBrowsableState.Never)] // don't have debugger view expand this
        override public DbDataRecord OriginalValues 
        { 
            get
            { 
                ValidateState();
                if (this.State == EntityState.Added)
                {
                    throw EntityUtil.OriginalValuesDoesNotExist(); 
                }
 
                return new ObjectStateEntryDbDataRecord(this); 
            }
        } 

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

        override public RelationshipManager RelationshipManager 
        {
            get
            {
                throw new InvalidOperationException(System.Data.Entity.Strings.ObjectStateEntry_RelationshipAndKeyEntriesDoNotHaveRelationshipManagers); 
            }
        } 
 
        public override void ChangeState(EntityState state)
        { 
            EntityUtil.CheckValidStateForChangeRelationshipState(state, "state");

            if (this.State == EntityState.Detached && state == EntityState.Detached)
            { 
                return;
            } 
 
            ValidateState();
 
            if (this.RelationshipWrapper.Key0 == this.Key0)
            {
                this.ObjectStateManager.ChangeRelationshipState(
                    this.Key0, this.Key1, 
                    this.RelationshipWrapper.AssociationSet.ElementType.FullName,
                    this.RelationshipWrapper.AssociationEndMembers[1].Name, 
                    state); 
            }
            else 
            {
                Debug.Assert(this.RelationshipWrapper.Key0 == this.Key1, "invalid relationship");
                this.ObjectStateManager.ChangeRelationshipState(
                    this.Key0, this.Key1, 
                    this.RelationshipWrapper.AssociationSet.ElementType.FullName,
                    this.RelationshipWrapper.AssociationEndMembers[0].Name, 
                    state); 
            }
        } 

        public override void ApplyCurrentValues(object currentEntity)
        {
            throw EntityUtil.CantModifyRelationValues(); 
        }
 
        public override void ApplyOriginalValues(object originalEntity) 
        {
            throw EntityUtil.CantModifyRelationValues(); 
        }

        #endregion
 
        #region ObjectStateEntry members
 
        override internal bool IsKeyEntry 
        {
            get 
            {
                return false;
            }
        } 

        override internal int GetFieldCount(StateManagerTypeMetadata metadata) 
        { 
            return _relationshipWrapper.AssociationEndMembers.Count;
        } 

        /// 
        /// Reuse or create a new (Entity)DataRecordInfo.
        ///  
        override internal DataRecordInfo GetDataRecordInfo(StateManagerTypeMetadata metadata, object userObject)
        { 
            //Dev Note: RelationshipType always has default facets. Thus its safe to construct a TypeUsage from EdmType 
            return new DataRecordInfo(TypeUsage.Create(((RelationshipSet)EntitySet).ElementType));
        } 

        override internal void SetModifiedAll()
        {
            ValidateState(); 
            throw EntityUtil.CantModifyRelationState();
        } 
 
        override internal Type GetFieldType(int ordinal, StateManagerTypeMetadata metadata)
        { 
            // 'metadata' is used for ComplexTypes in EntityEntry

            return typeof(EntityKey); // this is given By Design
        } 

        override internal string GetCLayerName(int ordinal, StateManagerTypeMetadata metadata) 
        { 
            ValidateRelationshipRange(ordinal);
            return _relationshipWrapper.AssociationEndMembers[ordinal].Name; 
        }

        override internal int GetOrdinalforCLayerName(string name, StateManagerTypeMetadata metadata)
        { 
            AssociationEndMember endMember;
            ReadOnlyMetadataCollection endMembers = _relationshipWrapper.AssociationEndMembers; 
            if (endMembers.TryGetValue(name, false, out endMember)) 
            {
                return endMembers.IndexOf(endMember); 
            }
            return -1;
        }
 
        override internal void RevertDelete()
        { 
            State = EntityState.Unchanged; 
            _cache.ChangeState(this, EntityState.Deleted, State);
        } 

        /// 
        /// 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 
        override internal void EntityMemberChanging(string entityMemberName) 
        {
            throw EntityUtil.CantModifyRelationValues(); 
        }

        /// 
        /// 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
        override internal void EntityMemberChanged(string entityMemberName) 
        {
            throw EntityUtil.CantModifyRelationValues();
        }
 
        /// 
        /// 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
        override internal void EntityComplexMemberChanging(string entityMemberName, object complexObject, string complexObjectMemberName)
        { 
            throw EntityUtil.CantModifyRelationValues();
        } 
 
        /// 
        /// 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
        override internal void EntityComplexMemberChanged(string entityMemberName, object complexObject, string complexObjectMemberName) 
        { 
            throw EntityUtil.CantModifyRelationValues();
        } 

        #endregion

        // Helper method to determine if the specified entityKey is in the given role and AssociationSet in this relationship entry 
        internal bool IsSameAssociationSetAndRole(AssociationSet associationSet, AssociationEndMember associationMember, EntityKey entityKey)
        { 
            Debug.Assert(associationSet.ElementType.AssociationEndMembers[0].Name == associationMember.Name || 
                         associationSet.ElementType.AssociationEndMembers[1].Name == associationMember.Name,
                         "Expected associationMember to be one of the ends of the specified associationSet."); 

            if (!Object.ReferenceEquals(_entitySet, associationSet))
            {
                return false; 
            }
 
            // Find the end of the relationship that corresponds to the associationMember and see if it matches the EntityKey we are looking for 
            if (_relationshipWrapper.AssociationSet.ElementType.AssociationEndMembers[0].Name == associationMember.Name)
            { 
                return entityKey == Key0;
            }
            else
            { 
                return entityKey == Key1;
            } 
        } 

        private object GetCurrentRelationValue(int ordinal, bool throwException) 
        {
            ValidateRelationshipRange(ordinal);
            ValidateState();
            if (State == EntityState.Deleted && throwException) 
            {
                throw EntityUtil.CurrentValuesDoesNotExist(); 
            } 
            return _relationshipWrapper.GetEntityKey(ordinal);
        } 

        private static void ValidateRelationshipRange(int ordinal)
        {
            if (unchecked(1u < (uint)ordinal)) 
            {
                throw EntityUtil.ArgumentOutOfRange("ordinal"); 
            } 
        }
 
        internal object GetCurrentRelationValue(int ordinal)
        {
            return GetCurrentRelationValue(ordinal, true);
        } 

        internal RelationshipWrapper RelationshipWrapper 
        { 
            get
            { 
                return _relationshipWrapper;
            }
            set
            { 
                Debug.Assert(null != value, "don't set wrapper to null");
                _relationshipWrapper = value; 
            } 
        }
 
        override internal void Reset()
        {
            _relationshipWrapper = null;
 
            base.Reset();
        } 
 
        /// 
        /// 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 
                    RelationshipWrapper = new RelationshipWrapper(RelationshipWrapper.AssociationSet, newKey);
                } 
                else
                {
                    RelationshipWrapper = new RelationshipWrapper(RelationshipWrapper, 0, newKey);
                } 
            }
            else 
            { 
                RelationshipWrapper = new RelationshipWrapper(RelationshipWrapper, 1, newKey);
            } 
        }

        internal void DeleteUnnecessaryKeyEntries()
        { 
            // 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; 
                EntityEntry relatedEntry = _cache.GetEntityEntry(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 (RelationshipEntry 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; 
                    }
                } 
            } 
        }
 
        //"doFixup" equals to False is called from EntityCollection & Ref code only
        internal void Delete(bool doFixup)
        {
            ValidateState(); 

            if (doFixup) 
            { 
                if (State != EntityState.Deleted)  //for deleted ObjectStateEntry its a no-op
                { 
                    //Find two ends of the relationship
                    EntityEntry entry1 = _cache.GetEntityEntry((EntityKey)GetCurrentRelationValue(0));
                    IEntityWrapper wrappedEntity1 = entry1.WrappedEntity;
                    EntityEntry entry2 = _cache.GetEntityEntry((EntityKey)GetCurrentRelationValue(1)); 
                    IEntityWrapper wrappedEntity2 = entry2.WrappedEntity;
 
                    // 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 (wrappedEntity1.Entity != null && wrappedEntity2.Entity != null) 
                    {
                        // Obtain the ro role name and relationship name
                        // We don't create a full NavigationRelationship here because that would require looking up
                        // additional information like property names that we don't need. 
                        ReadOnlyMetadataCollection endMembers = _relationshipWrapper.AssociationEndMembers;
                        string toRole = endMembers[1].Name; 
                        string relationshipName = ((AssociationSet)_entitySet).ElementType.FullName; 
                        wrappedEntity1.RelationshipManager.RemoveEntity(toRole, relationshipName, wrappedEntity2);
                    } 
                    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 (wrappedEntity1.Entity == null) 
                        {
                            targetKey = entry1.EntityKey; 
                            relationshipManager = wrappedEntity2.RelationshipManager;
                        }
                        else
                        { 
                            targetKey = entry2.EntityKey;
                            relationshipManager = wrappedEntity1.RelationshipManager; 
                        } 
                        Debug.Assert(relationshipManager != null, "Entity wrapper returned a null RelationshipManager");
 
                        // 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.RelationshipWrapper.GetAssociationEndMember(targetKey); 
                        EntityReference entityReference = (EntityReference)relationshipManager.GetRelatedEndInternal(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: 
                        // 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();
                        break; 
                    case EntityState.Modified: 
                        Debug.Assert(false, "RelationshipEntry cannot be in Modified state");
                        break; 
                    case EntityState.Unchanged:
                        _cache.ChangeState(this, EntityState.Unchanged, EntityState.Deleted);
                        State = EntityState.Deleted;
                        break; 
                    //case DataRowState.Deleted:  no-op
                } 
            } 
        }
 
        internal object GetOriginalRelationValue(int ordinal)
        {
            return GetCurrentRelationValue(ordinal, false);
        } 

        internal void DetachRelationshipEntry() 
        { 
            // no-op if already detached
            if (_cache != null) 
            {
                _cache.ChangeState(this, this.State, EntityState.Detached);
            }
        } 

        internal void ChangeRelationshipState(EntityEntry targetEntry, RelatedEnd relatedEnd, EntityState requestedState) 
        { 
            Debug.Assert(requestedState != EntityState.Modified, "Invalid requested state for relationsihp");
            Debug.Assert(this.State != EntityState.Modified, "Invalid initial state for relationsihp"); 

            EntityState initialState = this.State;

            switch (initialState) 
            {
                case EntityState.Added: 
                    switch (requestedState) 
                    {
                        case EntityState.Added: 
                            // no-op
                            break;
                        case EntityState.Unchanged:
                            this.AcceptChanges(); 
                            break;
                        case EntityState.Deleted: 
                            this.AcceptChanges(); 
                            // cascade deletion is not performed because TransactionManager.IsLocalPublicAPI == true
                            this.Delete(); 
                            break;
                        case EntityState.Detached:
                            // cascade deletion is not performed because TransactionManager.IsLocalPublicAPI == true
                            this.Delete(); 
                            break;
                        default: 
                            Debug.Assert(false, "Invalid requested state"); 
                            break;
                    } 
                    break;
                case EntityState.Unchanged:
                    switch (requestedState)
                    { 
                        case EntityState.Added:
                            this.ObjectStateManager.ChangeState(this, EntityState.Unchanged, EntityState.Added); 
                            this.State = EntityState.Added; 
                            break;
                        case EntityState.Unchanged: 
                            //no-op
                            break;
                        case EntityState.Deleted:
                            // cascade deletion is not performed because TransactionManager.IsLocalPublicAPI == true 
                            this.Delete();
                            break; 
                        case EntityState.Detached: 
                            // cascade deletion is not performed because TransactionManager.IsLocalPublicAPI == true
                            this.Delete(); 
                            this.AcceptChanges();
                            break;
                        default:
                            Debug.Assert(false, "Invalid requested state"); 
                            break;
                    } 
                    break; 
                case EntityState.Deleted:
                    switch (requestedState) 
                    {
                        case EntityState.Added:
                            relatedEnd.Add(targetEntry.WrappedEntity,
                                applyConstraints: true, 
                                addRelationshipAsUnchanged: false,
                                relationshipAlreadyExists: true, 
                                allowModifyingOtherEndOfRelationship: false, 
                                forceForeignKeyChanges: true);
                            this.ObjectStateManager.ChangeState(this, EntityState.Deleted, EntityState.Added); 
                            this.State = EntityState.Added;
                            break;
                        case EntityState.Unchanged:
                            relatedEnd.Add(targetEntry.WrappedEntity, 
                                applyConstraints: true,
                                addRelationshipAsUnchanged: false, 
                                relationshipAlreadyExists: true, 
                                allowModifyingOtherEndOfRelationship: false,
                                forceForeignKeyChanges: true); 
                            this.ObjectStateManager.ChangeState(this, EntityState.Deleted, EntityState.Unchanged);
                            this.State = EntityState.Unchanged;
                            break;
                        case EntityState.Deleted: 
                            // no-op
                            break; 
                        case EntityState.Detached: 
                            this.AcceptChanges();
                            break; 
                        default:
                            Debug.Assert(false, "Invalid requested state");
                            break;
                    } 
                    break;
                default: 
                    Debug.Assert(false, "Invalid entry state"); 
                    break;
            } 
        }


        #region RelationshipEnds as singly-linked list 

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

// 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.Generic; 
using System.Text;
using System.Diagnostics;
using System.Data.Objects.DataClasses;
using System.Data.Metadata.Edm; 
using System.Data.Objects.Internal;
using System.Data.Common; 
 
namespace System.Data.Objects
{ 
    internal sealed class RelationshipEntry : ObjectStateEntry
    {
#if DEBUG
        private static int _objectTypeCount; // Bid counter 
        internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
#endif 
 
        internal RelationshipWrapper _relationshipWrapper;
        internal EntityKey Key0 { get { return RelationshipWrapper.Key0; } } 
        internal EntityKey Key1 { get { return RelationshipWrapper.Key1; } }
        internal override System.Collections.BitArray ModifiedProperties
        {
            get { return null; } 
        }
 
        #region Linked list of related relationships 
        private RelationshipEntry _nextKey0;
        private RelationshipEntry _nextKey1; 
        #endregion

        #region Constructors
        internal RelationshipEntry(ObjectStateManager cache, EntityState state, RelationshipWrapper relationshipWrapper) 
            : base(cache, null, state)
        { 
            Debug.Assert(null != relationshipWrapper, "null RelationshipWrapper"); 
            Debug.Assert(EntityState.Added == state ||
                         EntityState.Unchanged == state || 
                         EntityState.Deleted == state,
                         "invalid EntityState");

            base._entitySet = relationshipWrapper.AssociationSet; 
            _relationshipWrapper = relationshipWrapper;
        } 
        #endregion 

        #region Public members 

        /// 
        /// API to accept the current values as original values and  mark the entity as Unchanged.
        ///  
        /// 
        ///  
        override public bool IsRelationship 
        {
            get 
            {
                ValidateState();
                return true;
            } 
        }
 
        override public void AcceptChanges() 
        {
            ValidateState(); 

            switch (State)
            {
                case EntityState.Deleted: 
                    DeleteUnnecessaryKeyEntries();
                    // 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:
                    _cache.ChangeState(this, EntityState.Added, EntityState.Unchanged); 
                    State = EntityState.Unchanged;
                    break; 
                case EntityState.Modified: 
                    Debug.Assert(false, "RelationshipEntry cannot be in Modified state");
                    break; 
                case EntityState.Unchanged:
                    break;
            }
        } 

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

        override public IEnumerable GetModifiedProperties() 
        { 
            ValidateState();
            yield break; 
        }

        override public void SetModified()
        { 
            ValidateState();
            throw EntityUtil.CantModifyRelationState(); 
        } 

        override public object Entity 
        {
            get
            {
                ValidateState(); 
                return null;
            } 
        } 

        override public EntityKey EntityKey 
        {
            get
            {
                ValidateState(); 
                return null;
            } 
            internal set 
            {
                // no-op for entires other than EntityEntry 
                Debug.Assert(false, "EntityKey setter shouldn't be called for RelationshipEntry");
            }
        }
 
        /// 
        /// Marks specified property as modified. 
        ///  
        /// This API recognizes the names in terms of OSpace
        /// If State is not Modified or Unchanged 
        ///
        override public void SetModifiedProperty(string propertyName)
        {
            ValidateState(); 

            throw EntityUtil.CantModifyRelationState(); 
        } 

        ///  
        /// Original values of entity
        /// 
        /// 
        ///  DbDataRecord  
        [DebuggerBrowsable(DebuggerBrowsableState.Never)] // don't have debugger view expand this
        override public DbDataRecord OriginalValues 
        { 
            get
            { 
                ValidateState();
                if (this.State == EntityState.Added)
                {
                    throw EntityUtil.OriginalValuesDoesNotExist(); 
                }
 
                return new ObjectStateEntryDbDataRecord(this); 
            }
        } 

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

        override public RelationshipManager RelationshipManager 
        {
            get
            {
                throw new InvalidOperationException(System.Data.Entity.Strings.ObjectStateEntry_RelationshipAndKeyEntriesDoNotHaveRelationshipManagers); 
            }
        } 
 
        public override void ChangeState(EntityState state)
        { 
            EntityUtil.CheckValidStateForChangeRelationshipState(state, "state");

            if (this.State == EntityState.Detached && state == EntityState.Detached)
            { 
                return;
            } 
 
            ValidateState();
 
            if (this.RelationshipWrapper.Key0 == this.Key0)
            {
                this.ObjectStateManager.ChangeRelationshipState(
                    this.Key0, this.Key1, 
                    this.RelationshipWrapper.AssociationSet.ElementType.FullName,
                    this.RelationshipWrapper.AssociationEndMembers[1].Name, 
                    state); 
            }
            else 
            {
                Debug.Assert(this.RelationshipWrapper.Key0 == this.Key1, "invalid relationship");
                this.ObjectStateManager.ChangeRelationshipState(
                    this.Key0, this.Key1, 
                    this.RelationshipWrapper.AssociationSet.ElementType.FullName,
                    this.RelationshipWrapper.AssociationEndMembers[0].Name, 
                    state); 
            }
        } 

        public override void ApplyCurrentValues(object currentEntity)
        {
            throw EntityUtil.CantModifyRelationValues(); 
        }
 
        public override void ApplyOriginalValues(object originalEntity) 
        {
            throw EntityUtil.CantModifyRelationValues(); 
        }

        #endregion
 
        #region ObjectStateEntry members
 
        override internal bool IsKeyEntry 
        {
            get 
            {
                return false;
            }
        } 

        override internal int GetFieldCount(StateManagerTypeMetadata metadata) 
        { 
            return _relationshipWrapper.AssociationEndMembers.Count;
        } 

        /// 
        /// Reuse or create a new (Entity)DataRecordInfo.
        ///  
        override internal DataRecordInfo GetDataRecordInfo(StateManagerTypeMetadata metadata, object userObject)
        { 
            //Dev Note: RelationshipType always has default facets. Thus its safe to construct a TypeUsage from EdmType 
            return new DataRecordInfo(TypeUsage.Create(((RelationshipSet)EntitySet).ElementType));
        } 

        override internal void SetModifiedAll()
        {
            ValidateState(); 
            throw EntityUtil.CantModifyRelationState();
        } 
 
        override internal Type GetFieldType(int ordinal, StateManagerTypeMetadata metadata)
        { 
            // 'metadata' is used for ComplexTypes in EntityEntry

            return typeof(EntityKey); // this is given By Design
        } 

        override internal string GetCLayerName(int ordinal, StateManagerTypeMetadata metadata) 
        { 
            ValidateRelationshipRange(ordinal);
            return _relationshipWrapper.AssociationEndMembers[ordinal].Name; 
        }

        override internal int GetOrdinalforCLayerName(string name, StateManagerTypeMetadata metadata)
        { 
            AssociationEndMember endMember;
            ReadOnlyMetadataCollection endMembers = _relationshipWrapper.AssociationEndMembers; 
            if (endMembers.TryGetValue(name, false, out endMember)) 
            {
                return endMembers.IndexOf(endMember); 
            }
            return -1;
        }
 
        override internal void RevertDelete()
        { 
            State = EntityState.Unchanged; 
            _cache.ChangeState(this, EntityState.Deleted, State);
        } 

        /// 
        /// 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 
        override internal void EntityMemberChanging(string entityMemberName) 
        {
            throw EntityUtil.CantModifyRelationValues(); 
        }

        /// 
        /// 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
        override internal void EntityMemberChanged(string entityMemberName) 
        {
            throw EntityUtil.CantModifyRelationValues();
        }
 
        /// 
        /// 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
        override internal void EntityComplexMemberChanging(string entityMemberName, object complexObject, string complexObjectMemberName)
        { 
            throw EntityUtil.CantModifyRelationValues();
        } 
 
        /// 
        /// 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
        override internal void EntityComplexMemberChanged(string entityMemberName, object complexObject, string complexObjectMemberName) 
        { 
            throw EntityUtil.CantModifyRelationValues();
        } 

        #endregion

        // Helper method to determine if the specified entityKey is in the given role and AssociationSet in this relationship entry 
        internal bool IsSameAssociationSetAndRole(AssociationSet associationSet, AssociationEndMember associationMember, EntityKey entityKey)
        { 
            Debug.Assert(associationSet.ElementType.AssociationEndMembers[0].Name == associationMember.Name || 
                         associationSet.ElementType.AssociationEndMembers[1].Name == associationMember.Name,
                         "Expected associationMember to be one of the ends of the specified associationSet."); 

            if (!Object.ReferenceEquals(_entitySet, associationSet))
            {
                return false; 
            }
 
            // Find the end of the relationship that corresponds to the associationMember and see if it matches the EntityKey we are looking for 
            if (_relationshipWrapper.AssociationSet.ElementType.AssociationEndMembers[0].Name == associationMember.Name)
            { 
                return entityKey == Key0;
            }
            else
            { 
                return entityKey == Key1;
            } 
        } 

        private object GetCurrentRelationValue(int ordinal, bool throwException) 
        {
            ValidateRelationshipRange(ordinal);
            ValidateState();
            if (State == EntityState.Deleted && throwException) 
            {
                throw EntityUtil.CurrentValuesDoesNotExist(); 
            } 
            return _relationshipWrapper.GetEntityKey(ordinal);
        } 

        private static void ValidateRelationshipRange(int ordinal)
        {
            if (unchecked(1u < (uint)ordinal)) 
            {
                throw EntityUtil.ArgumentOutOfRange("ordinal"); 
            } 
        }
 
        internal object GetCurrentRelationValue(int ordinal)
        {
            return GetCurrentRelationValue(ordinal, true);
        } 

        internal RelationshipWrapper RelationshipWrapper 
        { 
            get
            { 
                return _relationshipWrapper;
            }
            set
            { 
                Debug.Assert(null != value, "don't set wrapper to null");
                _relationshipWrapper = value; 
            } 
        }
 
        override internal void Reset()
        {
            _relationshipWrapper = null;
 
            base.Reset();
        } 
 
        /// 
        /// 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 
                    RelationshipWrapper = new RelationshipWrapper(RelationshipWrapper.AssociationSet, newKey);
                } 
                else
                {
                    RelationshipWrapper = new RelationshipWrapper(RelationshipWrapper, 0, newKey);
                } 
            }
            else 
            { 
                RelationshipWrapper = new RelationshipWrapper(RelationshipWrapper, 1, newKey);
            } 
        }

        internal void DeleteUnnecessaryKeyEntries()
        { 
            // 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; 
                EntityEntry relatedEntry = _cache.GetEntityEntry(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 (RelationshipEntry 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; 
                    }
                } 
            } 
        }
 
        //"doFixup" equals to False is called from EntityCollection & Ref code only
        internal void Delete(bool doFixup)
        {
            ValidateState(); 

            if (doFixup) 
            { 
                if (State != EntityState.Deleted)  //for deleted ObjectStateEntry its a no-op
                { 
                    //Find two ends of the relationship
                    EntityEntry entry1 = _cache.GetEntityEntry((EntityKey)GetCurrentRelationValue(0));
                    IEntityWrapper wrappedEntity1 = entry1.WrappedEntity;
                    EntityEntry entry2 = _cache.GetEntityEntry((EntityKey)GetCurrentRelationValue(1)); 
                    IEntityWrapper wrappedEntity2 = entry2.WrappedEntity;
 
                    // 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 (wrappedEntity1.Entity != null && wrappedEntity2.Entity != null) 
                    {
                        // Obtain the ro role name and relationship name
                        // We don't create a full NavigationRelationship here because that would require looking up
                        // additional information like property names that we don't need. 
                        ReadOnlyMetadataCollection endMembers = _relationshipWrapper.AssociationEndMembers;
                        string toRole = endMembers[1].Name; 
                        string relationshipName = ((AssociationSet)_entitySet).ElementType.FullName; 
                        wrappedEntity1.RelationshipManager.RemoveEntity(toRole, relationshipName, wrappedEntity2);
                    } 
                    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 (wrappedEntity1.Entity == null) 
                        {
                            targetKey = entry1.EntityKey; 
                            relationshipManager = wrappedEntity2.RelationshipManager;
                        }
                        else
                        { 
                            targetKey = entry2.EntityKey;
                            relationshipManager = wrappedEntity1.RelationshipManager; 
                        } 
                        Debug.Assert(relationshipManager != null, "Entity wrapper returned a null RelationshipManager");
 
                        // 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.RelationshipWrapper.GetAssociationEndMember(targetKey); 
                        EntityReference entityReference = (EntityReference)relationshipManager.GetRelatedEndInternal(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: 
                        // 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();
                        break; 
                    case EntityState.Modified: 
                        Debug.Assert(false, "RelationshipEntry cannot be in Modified state");
                        break; 
                    case EntityState.Unchanged:
                        _cache.ChangeState(this, EntityState.Unchanged, EntityState.Deleted);
                        State = EntityState.Deleted;
                        break; 
                    //case DataRowState.Deleted:  no-op
                } 
            } 
        }
 
        internal object GetOriginalRelationValue(int ordinal)
        {
            return GetCurrentRelationValue(ordinal, false);
        } 

        internal void DetachRelationshipEntry() 
        { 
            // no-op if already detached
            if (_cache != null) 
            {
                _cache.ChangeState(this, this.State, EntityState.Detached);
            }
        } 

        internal void ChangeRelationshipState(EntityEntry targetEntry, RelatedEnd relatedEnd, EntityState requestedState) 
        { 
            Debug.Assert(requestedState != EntityState.Modified, "Invalid requested state for relationsihp");
            Debug.Assert(this.State != EntityState.Modified, "Invalid initial state for relationsihp"); 

            EntityState initialState = this.State;

            switch (initialState) 
            {
                case EntityState.Added: 
                    switch (requestedState) 
                    {
                        case EntityState.Added: 
                            // no-op
                            break;
                        case EntityState.Unchanged:
                            this.AcceptChanges(); 
                            break;
                        case EntityState.Deleted: 
                            this.AcceptChanges(); 
                            // cascade deletion is not performed because TransactionManager.IsLocalPublicAPI == true
                            this.Delete(); 
                            break;
                        case EntityState.Detached:
                            // cascade deletion is not performed because TransactionManager.IsLocalPublicAPI == true
                            this.Delete(); 
                            break;
                        default: 
                            Debug.Assert(false, "Invalid requested state"); 
                            break;
                    } 
                    break;
                case EntityState.Unchanged:
                    switch (requestedState)
                    { 
                        case EntityState.Added:
                            this.ObjectStateManager.ChangeState(this, EntityState.Unchanged, EntityState.Added); 
                            this.State = EntityState.Added; 
                            break;
                        case EntityState.Unchanged: 
                            //no-op
                            break;
                        case EntityState.Deleted:
                            // cascade deletion is not performed because TransactionManager.IsLocalPublicAPI == true 
                            this.Delete();
                            break; 
                        case EntityState.Detached: 
                            // cascade deletion is not performed because TransactionManager.IsLocalPublicAPI == true
                            this.Delete(); 
                            this.AcceptChanges();
                            break;
                        default:
                            Debug.Assert(false, "Invalid requested state"); 
                            break;
                    } 
                    break; 
                case EntityState.Deleted:
                    switch (requestedState) 
                    {
                        case EntityState.Added:
                            relatedEnd.Add(targetEntry.WrappedEntity,
                                applyConstraints: true, 
                                addRelationshipAsUnchanged: false,
                                relationshipAlreadyExists: true, 
                                allowModifyingOtherEndOfRelationship: false, 
                                forceForeignKeyChanges: true);
                            this.ObjectStateManager.ChangeState(this, EntityState.Deleted, EntityState.Added); 
                            this.State = EntityState.Added;
                            break;
                        case EntityState.Unchanged:
                            relatedEnd.Add(targetEntry.WrappedEntity, 
                                applyConstraints: true,
                                addRelationshipAsUnchanged: false, 
                                relationshipAlreadyExists: true, 
                                allowModifyingOtherEndOfRelationship: false,
                                forceForeignKeyChanges: true); 
                            this.ObjectStateManager.ChangeState(this, EntityState.Deleted, EntityState.Unchanged);
                            this.State = EntityState.Unchanged;
                            break;
                        case EntityState.Deleted: 
                            // no-op
                            break; 
                        case EntityState.Detached: 
                            this.AcceptChanges();
                            break; 
                        default:
                            Debug.Assert(false, "Invalid requested state");
                            break;
                    } 
                    break;
                default: 
                    Debug.Assert(false, "Invalid entry state"); 
                    break;
            } 
        }


        #region RelationshipEnds as singly-linked list 

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

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