RelatedEnd.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / ndp / fx / src / DataEntity / System / Data / Objects / DataClasses / RelatedEnd.cs / 5 / RelatedEnd.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner       [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 
using System.Collections;
using System.Collections.Generic; 
using System.ComponentModel;
using System.Data;
using System.Data.Common;
using System.Data.Common.CommandTrees; 
using System.Data.Common.Utils;
using System.Data.Metadata.Edm; 
using System.Data.Objects.Internal; 
using System.Diagnostics;
using System.Text; 
using System.Runtime.Serialization;

namespace System.Data.Objects.DataClasses
{ 
    /// 
    /// Base class for EntityCollection and EntityReference 
    ///  

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] 
    [DataContract]
    [Serializable]
    public abstract class RelatedEnd : IRelatedEnd
    { 

        //----------------- 
        // Internal Constructors 
        //-----------------
 
        /// 
        /// The default constructor is required for some serialization scenarios with EntityReference.
        /// 
        internal RelatedEnd() 
        {
        } 
 
        internal RelatedEnd(IEntityWithRelationships owner, RelationshipNavigation navigation, IRelationshipFixer relationshipFixer)
        { 
            EntityUtil.CheckArgumentNull(owner, "owner");
            EntityUtil.CheckArgumentNull(navigation, "navigation");
            EntityUtil.CheckArgumentNull(relationshipFixer, "fixer");
 
            InitializeRelatedEnd(owner, navigation, relationshipFixer);
        } 
 
        // ------
        // Fields 
        // ------
        private const string _entityKeyParamName = "EntityKeyValue";

        // The following fields are serialized.  Adding or removing a serialized field is considered 
        // a breaking change.  This includes changing the field type or field name of existing
        // serialized fields. If you need to make this kind of change, it may be possible, but it 
        // will require some custom serialization/deserialization code. 
        // These fields should not be changed once they have been initialized with non-null values, but they can't be read-only because there
        // are serialization scenarios where they have to be set after construction 
        private IEntityWithRelationships _owner;
        private RelationshipNavigation _navigation;
        private IRelationshipFixer _relationshipFixer;
 
        internal bool _isLoaded;
 
        // The fields in this group are set only when attached to a context, so we don't need to serialize. 
        [NonSerialized]
        private RelationshipSet _relationshipSet; 
        [NonSerialized]
        private ObjectContext _context;
        [NonSerialized]
        private bool _usingNoTracking; 
        [NonSerialized]
        private RelationshipType _relationMetadata; 
        [NonSerialized] 
        private RelationshipEndMember _fromEndProperty; //owner end property
        [NonSerialized] 
        private RelationshipEndMember _toEndProperty;
        [NonSerialized]
        private string _sourceQuery;
 
        [NonSerialized]
        internal bool _suppressEvents; 
        [NonSerialized] 
        internal CollectionChangeEventHandler _onAssociationChanged;
 

        // ------
        // Events
        // ------ 

        ///  
        /// Event to notify changes in the Associations. 
        /// 
        public event CollectionChangeEventHandler AssociationChanged 
        {
            add
            {
                CheckOwnerNull(); 
                _onAssociationChanged += value;
            } 
            remove 
            {
                CheckOwnerNull(); 
                _onAssociationChanged -= value;
            }
        }
 
        /// internal event to notify change in collection
        internal virtual event CollectionChangeEventHandler AssociationChangedForObjectView 
        { 
            // we fire this event only from EntityCollection, definetely not from EntityReference
            add { Debug.Assert(false, "should never happen"); } 
            remove { Debug.Assert(false, "should never happen"); }
        }

 
        // ----------
        // Properties 
        // ---------- 

        ///  
        /// This class describes a relationship navigation from the
        /// navigation property on one entity to another entity.
        /// RelationshipNavigation uniquely identify a relationship type.
        /// The RelationshipNavigation class is internal only, so this property is also internal. 
        /// See RelationshipName, SourceRoleName, and TargetRoleName for the public exposure
        /// of the information contained in this RelationshipNavigation. 
        ///  
        internal RelationshipNavigation RelationshipNavigation
        { 
            get
            {
                return _navigation;
            } 
        }
 
        ///  
        /// Name of the relationship in which this RelatedEnd is participating
        ///  
        [System.Xml.Serialization.SoapIgnore]
        [System.Xml.Serialization.XmlIgnore]
        public string RelationshipName
        { 
            get
            { 
                CheckOwnerNull(); 
                return _navigation.RelationshipName;
            } 
        }

        /// 
        /// Name of the relationship source role used to generate this RelatedEnd 
        /// 
        [System.Xml.Serialization.SoapIgnore] 
        [System.Xml.Serialization.XmlIgnore] 
        public string SourceRoleName
        { 
            get
            {
                CheckOwnerNull();
                return _navigation.From; 
            }
        } 
 
        /// 
        /// Name of the relationship target role used to generate this RelatedEnd 
        /// 
        [System.Xml.Serialization.SoapIgnore]
        [System.Xml.Serialization.XmlIgnore]
        public string TargetRoleName 
        {
            get 
            { 
                CheckOwnerNull();
                return _navigation.To; 
            }
        }

        IEnumerable IRelatedEnd.CreateSourceQuery() 
        {
            CheckOwnerNull(); 
            return this.CreateSourceQueryInternal(); 
        }
 
        internal IEntityWithRelationships Owner
        {
            get
            { 
                return this._owner;
            } 
        } 

        internal ObjectContext ObjectContext 
        {
            get
            {
                return this._context; 
            }
        } 
 
        internal virtual void BulkDeleteAll(System.Collections.Generic.List list)
        { 
            throw EntityUtil.NotSupported();
        }

        ///  
        /// Returns the relationship metadata associated with this RelatedEnd.
        /// This value is available once the RelatedEnd is attached to an ObjectContext 
        /// or is retrieved with MergeOption.NoTracking 
        /// 
        [System.Xml.Serialization.SoapIgnore] 
        [System.Xml.Serialization.XmlIgnore]
        public RelationshipSet RelationshipSet
        {
            get 
            {
                CheckOwnerNull(); 
                return this._relationshipSet; 
            }
        } 

        internal RelationshipType RelationMetadata
        {
            get 
            {
                return this._relationMetadata; 
            } 
        }
 
        internal RelationshipEndMember ToEndMember
        {
            get
            { 
                return this._toEndProperty;
            } 
        } 

        internal bool UsingNoTracking 
        {
            get
            {
                return this._usingNoTracking; 
            }
        } 
 
        internal MergeOption DefaultMergeOption
        { 
            get
            {
                return UsingNoTracking ? MergeOption.NoTracking : MergeOption.AppendOnly;
            } 
        }
 
        internal RelationshipEndMember FromEndProperty 
        {
            get 
            {
                return this._fromEndProperty;
            }
        } 

        ///  
        /// IsLoaded returns true if and only if Load was called. 
        /// 
        [System.Xml.Serialization.SoapIgnore] 
        [System.Xml.Serialization.XmlIgnore]
        public bool IsLoaded
        {
            get 
            {
                CheckOwnerNull(); 
                return this._isLoaded; 
            }
        } 

        internal void SetIsLoaded(bool value)
        {
            this._isLoaded = value; 
        }
 
        ///  
        /// This is the query which represents the source of the
        /// related end.  It is constructed on demand using the 
        /// _connection and _cache fields and a query string based on
        /// the type of related end and the metadata passed into its
        /// constructor indicating the particular EDM construct the
        /// related end models. This method is called by both subclasses of this type 
        /// and those subclasses pass in their generic type parameter in order
        /// to produce an ObjectQuery of the right type. This allows this common 
        /// functionality to be implemented here in the base class while still 
        /// allowing the base class to be non-generic.
        ///  
        /// MergeOption to use when creating the query
        internal ObjectQuery CreateSourceQuery(MergeOption mergeOption)
        {
            // must have a context 
            if (_context == null)
            { 
                return null; 
            }
 
            ObjectStateEntry stateEntry = _context.ObjectStateManager.FindObjectStateEntry(_owner);
            EntityState entityState;
            if (stateEntry == null)
            { 
                if (UsingNoTracking)
                { 
                    entityState = EntityState.Detached; 
                }
                else 
                {
                    throw EntityUtil.InvalidEntityStateSource();
                }
            } 
            else
            { 
                Debug.Assert(stateEntry != null, "Entity should exist in the current context"); 
                entityState = stateEntry.State;
            } 

            //Throw incase entity is in added state
            if (entityState == EntityState.Added)
            { 
                throw EntityUtil.InvalidEntityStateSource();
            } 
 
            Debug.Assert(!(entityState != EntityState.Detached && UsingNoTracking), "Entity with NoTracking option cannot exist in the ObjectStateManager");
 
            // the CreateSourceQuery method can only return non-NULL when we're
            // either detached & mergeOption is NoTracking or
            // Modified/Unchanged/Deleted and mergeOption is NOT NoTracking
            // (if entity is attached to the context, mergeOption should never be NoTracking) 
            if (!((entityState == EntityState.Detached && UsingNoTracking) ||
                   entityState == EntityState.Modified || 
                   entityState == EntityState.Unchanged || 
                   entityState == EntityState.Deleted))
            { 
                return null;
            }

            // Construct a new source query and return it. 
            Debug.Assert(_relationshipSet != null, "If we are attached to a context, we should have a relationship set.");
 
            // Retrieve the entity key of the owner. 
            EntityKey key = _context.ObjectStateManager.GetEntityKey(_owner);
 
            // If the source query text has not be initialized, then do so now.
            if (null == _sourceQuery)
            {
                // Translate to: 
                // SELECT VALUE [TargetEntity]
                //  FROM 
                //      (SELECT VALUE x FROM ##RelationshipSet AS x 
                //       WHERE Key(x.[##SourceRoleName]) = ROW(@key1 AS key1[..., @keyN AS keyN])
                //       ) AS [AssociationEntry] 
                //  INNER JOIN
                //       OfType(##TargetEntityset, ##TargetRole.EntityType) AS [TargetEntity]
                //  ON
                //       Key([AssociationEntry].##TargetRoleName) = Key(Ref([TargetEntity])) 
                //
                // Note that the OfType operator can be omitted if the element type of ##TargetEntitySet 
                // is equal to the Entity type produced by the target end of the relationship. 
                Debug.Assert(_relationshipSet.BuiltInTypeKind == BuiltInTypeKind.AssociationSet, "Non-AssociationSet Relationship Set?");
                EntitySet targetEntitySet = ((AssociationSet)_relationshipSet).AssociationSetEnds[_toEndProperty.Name].EntitySet; 

                StringBuilder sourceBuilder = new StringBuilder("SELECT VALUE [TargetEntity] FROM (SELECT VALUE x FROM ");
                sourceBuilder.Append("[");
                sourceBuilder.Append(_relationshipSet.EntityContainer.Name); 
                sourceBuilder.Append("].[");
                sourceBuilder.Append(_relationshipSet.Name); 
                sourceBuilder.Append("] AS x WHERE Key(x.["); 
                sourceBuilder.Append(_fromEndProperty.Name);
                sourceBuilder.Append("]) = ROW("); 

                AliasGenerator keyParamNameGen = new AliasGenerator(_entityKeyParamName); // Aliases are cached in AliasGenerator
                int keyMemberCount = key.GetEntitySet(ObjectContext.MetadataWorkspace).ElementType.KeyMembers.Count;
                for(int idx = 0; idx < keyMemberCount; idx++) 
                {
                    string keyParamName = keyParamNameGen.Next(); 
                    sourceBuilder.Append("@"); 
                    sourceBuilder.Append(keyParamName);
                    sourceBuilder.Append(" AS "); 
                    sourceBuilder.Append(keyParamName);

                    if(idx < keyMemberCount - 1)
                    { 
                        sourceBuilder.Append(",");
                    } 
                } 

                sourceBuilder.Append(")) AS [AssociationEntry] INNER JOIN "); 

                EntityType targetEntityType = MetadataHelper.GetEntityTypeForEnd((AssociationEndMember)_toEndProperty);
                bool ofTypeRequired = false;
                if(!targetEntitySet.ElementType.EdmEquals(targetEntityType) && 
                    !TypeSemantics.IsSubTypeOf(targetEntitySet.ElementType, targetEntityType))
                { 
                    // If the type contained in the target entity set is not equal to 
                    // or a subtype of the referenced type, then an OfType must be
                    // applied to the target entityset to yield only those elements that 
                    // are of the referenced type or a subtype of the referenced type.
                    ofTypeRequired = true;

                    // The type name used in the OfType clause must be the name of the 
                    // corresponding O-Space Entity type, since the source query will be
                    // parsed using the CLR perspective (by ObjectQuery). 
                    TypeUsage targetOSpaceTypeUsage = ObjectContext.MetadataWorkspace.GetOSpaceTypeUsage(TypeUsage.Create(targetEntityType)); 
                    targetEntityType = (EntityType)targetOSpaceTypeUsage.EdmType;
                } 

                if(ofTypeRequired)
                {
                    sourceBuilder.Append("OfType("); 
                }
                sourceBuilder.Append("["); 
                sourceBuilder.Append(targetEntitySet.EntityContainer.Name); 
                sourceBuilder.Append("].[");
                sourceBuilder.Append(targetEntitySet.Name); 
                sourceBuilder.Append("]");
                if(ofTypeRequired)
                {
                    sourceBuilder.Append(", ["); 
                    if (targetEntityType.NamespaceName != string.Empty)
                    { 
                        sourceBuilder.Append(targetEntityType.NamespaceName); 
                        sourceBuilder.Append("].[");
                    } 
                    sourceBuilder.Append(targetEntityType.Name);
                    sourceBuilder.Append("])");
                }
                sourceBuilder.Append(" AS [TargetEntity] ON Key([AssociationEntry].["); 
                sourceBuilder.Append(_toEndProperty.Name);
                sourceBuilder.Append("]) = Key(Ref([TargetEntity]))"); 
 
                _sourceQuery = sourceBuilder.ToString();
            } 

            // Create a new ObjectQuery based on the source query text, the object context, and the specfied merge option.
            ObjectQuery query = new ObjectQuery(_sourceQuery, _context, mergeOption);
 
            // Add a parameter for each entity key value found on the key.
            AliasGenerator paramNameGen = new AliasGenerator(_entityKeyParamName); // Aliases are cached in AliasGenerator 
            ReadOnlyMetadataCollection.Enumerator keyMembers = key.GetEntitySet(ObjectContext.MetadataWorkspace).ElementType.KeyMembers.GetEnumerator(); 
            foreach (EntityKeyMember keyValue in key.EntityKeyValues)
            { 
                // Move on to the key member that corresponds to the current key value.
                keyMembers.MoveNext();

                // Create a new ObjectParameter with the next parameter name and the next key value. 
                ObjectParameter queryParam = new ObjectParameter(paramNameGen.Next(), keyValue.Value);
 
                // Map the type of the key member to S-Space and explicitly specify this mapped type 
                // as the effective type of the new ObjectParameter - this is required so that the
                // type of the key value parameter matches the declared type of the key member when 
                // the query text is parsed.
                queryParam.TypeUsage = Helper.GetModelTypeUsage(keyMembers.Current);

                // Add the new parameter to the Parameters collection of the query. 
                query.Parameters.Add(queryParam);
            } 
 
            // It should not be possible to add or remove parameters from the new query, since the query text
            // is fixed. Adding or removing parameters will likely make the query fail to execute. 
            query.Parameters.SetReadOnly(true);

            // Return the new ObjectQuery. Note that this is intentionally a tear-off so that any changes made
            // to its Parameters collection (or the ObjectParameters themselves) have no effect on anyone else 
            // that may retrieve this query - each access will always return a new ObjectQuery instance.
            return query; 
        } 

        ///  
        /// Validates that a call to Load has the correct conditions
        /// This helps to reduce the complexity of the Load call (SQLBU 524128)
        /// 
        /// See RelatedEnd.CreateSourceQuery method. This is returned here so we can create it and validate the state before returning it to the caller 
        protected ObjectQuery ValidateLoad(MergeOption mergeOption, string relatedEndName)
        { 
            ObjectQuery sourceQuery = CreateSourceQuery(mergeOption); 
            if (null == sourceQuery)
            { 
                throw EntityUtil.RelatedEndNotAttachedToContext(relatedEndName);
            }

            ObjectStateEntry entry = ObjectContext.ObjectStateManager.FindObjectStateEntry(this.Owner); 
            //Throw in case entity is in deleted state
            if (entry != null && entry.State == EntityState.Deleted) 
            { 
                throw EntityUtil.InvalidEntityStateLoad(relatedEndName);
            } 

            // MergeOption for Load must be NoTracking if and only if the source entity was NoTracking. If the source entity was
            // retrieved with any other MergeOption, the Load MergeOption can be anything but NoTracking. I.e. The entity could
            // have been loaded with OverwriteChanges and the Load option can be AppendOnly. 
            if (UsingNoTracking != (mergeOption == MergeOption.NoTracking))
            { 
                throw EntityUtil.MismatchedMergeOptionOnLoad(mergeOption); 
            }
 
            if (UsingNoTracking)
            {
                if (this.IsLoaded)
                { 
                    throw EntityUtil.LoadCalledOnAlreadyLoadedNoTrackedRelatedEnd();
                } 
 
                if (!IsEmpty())
                { 
                    throw EntityUtil.LoadCalledOnNonEmptyNoTrackedRelatedEnd();
                }
            }
 
            return sourceQuery;
        } 
 
        // -------
        // Methods 
        // -------

        /// 
        /// Loads the related entity or entities into the local related end using the default merge option. 
        /// 
        public void Load() 
        { 
            CheckOwnerNull();
            Load(DefaultMergeOption); 
        }

        /// 
        /// Loads the related entity or entities into the local related end using the supplied MergeOption. 
        /// 
        public abstract void Load(MergeOption mergeOption); 
 

        ///  
        /// Takes a list of related entities and merges them into the current collection.
        /// 
        /// Entities to relate to the owner of this EntityCollection
        /// MergeOption to use when updating existing relationships 
        /// Indicates whether IsLoaded should be set to true after the Load is complete.
        /// Should be false in cases where we cannot guarantee that the set of entities is complete 
        /// and matches the server, such as Attach. 
        protected void Merge(IEnumerable collection, MergeOption mergeOption, bool setIsLoaded)
        { 
            //Dev note: do not add event firing in Merge API, if it need to be added, add it to the caller

            List refreshedCollection = collection as List;
            if (null == refreshedCollection) 
            {
                refreshedCollection = new List(collection); 
            } 

            EntityKey sourceKey = ObjectStateManager.FindKeyOnEntityWithRelationships(Owner); 
            EntityUtil.CheckEntityKeyNull(sourceKey);

            ObjectStateManager.UpdateRelationships(this.ObjectContext, mergeOption, (AssociationSet)RelationshipSet, (AssociationEndMember)FromEndProperty, sourceKey, this.Owner, (AssociationEndMember)ToEndMember, refreshedCollection, setIsLoaded);
 
            if (setIsLoaded)
            { 
                // If the input collection contains all related entities, mark the collection as "loaded" 
                _isLoaded = true;
            } 
        }

        /// 
        /// Attaches an entity to the related end. If the related end is already filled 
        /// or partially filled, this merges the existing entities with the given entity. The given
        /// entity is not assumed to be the complete set of related entities. 
        /// 
        /// Owner and all entities passed in must be in Unchanged or Modified state.
        /// Deleted elements are allowed only when the state manager is already tracking the relationship 
        /// instance.
        /// 
        /// The entity to attach to the related end
        /// Thrown when  is null. 
        /// Thrown when the entity cannot be related via the current relationship end.
        void IRelatedEnd.Attach(IEntityWithRelationships entity) 
        { 
            CheckOwnerNull();
            EntityUtil.CheckArgumentNull(entity, "entity"); 
            Attach(new IEntityWithRelationships[] { entity }, false);
        }

        internal protected void Attach(IEnumerable entities, bool allowCollection) 
        {
            ValidateOwnerForAttach(); 
 
            // validate children and collect them in the "refreshedCollection" for this instance
            int index = 0; 
            List collection = new List();

            foreach (TEntity entity in entities)
            { 
                ValidateEntityForAttach(entity, index++, allowCollection);
                collection.Add(entity); 
            } 

            _suppressEvents = true; 
            try
            {
                // After Attach, the two entities should be related in the Unchanged state, so use OverwriteChanges
                // Since no query is done in this case, the MergeOption only controls the relationships 
                Merge(collection, MergeOption.OverwriteChanges, false /*setIsLoaded*/);
            } 
            finally 
            {
                _suppressEvents = false; 
            }
            OnAssociationChanged(CollectionChangeAction.Refresh, null);
        }
 
        // verifies requirements for Owner in Attach()
        internal protected void ValidateOwnerForAttach() 
        { 
            if (null == this.ObjectContext || UsingNoTracking)
            { 
                throw EntityUtil.InvalidOwnerStateForAttach();
            }

            // find state entry 
            ObjectStateEntry stateEntry = this.ObjectContext.ObjectStateManager.GetObjectStateEntry(this.Owner);
            if (stateEntry.State != EntityState.Modified && 
                stateEntry.State != EntityState.Unchanged) 
            {
                throw EntityUtil.InvalidOwnerStateForAttach(); 
            }
        }

        // verifies requirements for child entity passed to Attach() 
        internal protected void ValidateEntityForAttach(TEntity entity, int index, bool allowCollection)
        { 
            if (null == entity) 
            {
                if (allowCollection) 
                {
                    throw EntityUtil.InvalidNthElementNullForAttach(index);
                }
                else 
                {
                    throw EntityUtil.ArgumentNull("entity"); 
                } 
            }
 
            // verify the entity exists in the current context
            Debug.Assert(null != this.ObjectContext,
                "ObjectContext must not be null after call to ValidateOwnerForAttach");
            Debug.Assert(!UsingNoTracking, "We should not be here for NoTracking case."); 
            ObjectStateEntry stateEntry = this.ObjectContext.ObjectStateManager.FindObjectStateEntry(entity);
            if (null == stateEntry || !Object.ReferenceEquals(stateEntry.Entity, entity)) 
            { 
                if (allowCollection)
                { 
                    throw EntityUtil.InvalidNthElementContextForAttach(index);
                }
                else
                { 
                    throw EntityUtil.InvalidEntityContextForAttach();
                } 
            } 
            Debug.Assert(stateEntry.State != EntityState.Detached,
                "State cannot be detached if the entry was retrieved from the context"); 

            // verify the state of the entity (may not be in added state, since we only support attaching relationships
            // to existing entities)
            if (stateEntry.State != EntityState.Unchanged && 
                stateEntry.State != EntityState.Modified)
            { 
                if (allowCollection) 
                {
                    throw EntityUtil.InvalidNthElementStateForAttach(index); 
                }
                else
                {
                    throw EntityUtil.InvalidEntityStateForAttach(); 
                }
            } 
        } 

        internal abstract IEnumerable CreateSourceQueryInternal(); 

        /// 
        ///   Adds an entity to the related end.  If the owner is
        ///   attached to a cache then the all the connected ends are 
        ///   added to the object cache and their corresponding relationships
        ///   are also added to the ObjectStateManager. The RelatedEnd of the 
        ///   relationship is also fixed. 
        /// 
        ///  
        ///   Entity instance of type IEntityWithRelationships to add to the related end
        /// 
        void IRelatedEnd.Add(IEntityWithRelationships entity)
        { 
            EntityUtil.CheckArgumentNull(entity, "entity");
            if (_owner != null) 
            { 
                Add(entity, /*applyConstraints*/true);
            } 
            else
            {
                // The related end is in a disconnected state, so the related end is just a container
                // A common scenario for this is during WCF deserialization 
                DisconnectedAdd(entity);
            } 
        } 

        ///  
        ///   Removes an entity from the related end.  If owner is
        ///   attached to a cache, marks relationship for deletion and if
        ///   the relationship is composition also marks the entity for deletion.
        ///  
        /// 
        ///   Entity instance of type IEntityWithRelationships to remove from the related end 
        ///  
        /// Returns true if the entity was successfully removed, false if the entity was not part of the RelatedEnd.
        bool IRelatedEnd.Remove(IEntityWithRelationships entity) 
        {
            EntityUtil.CheckArgumentNull(entity, "entity");

            if (_owner != null) 
            {
                if (this.ContainsEntity(entity)) 
                { 

                    Remove(entity, /*fixup*/true, /*deleteEntity*/false, /*deleteOwner*/false, /*applyReferentialConstraints*/true); 
                    return true;
                }
                // The entity is not related so return false
                return false; 

            } 
            else 
            {
                // The related end is in a disconncted state, so the related end is just a container 
                // A common scenario for this is during WCF deserialization
                return DisconnectedRemove(entity);
            }
        } 

        internal abstract void DisconnectedAdd(IEntityWithRelationships entity); 
        internal abstract bool DisconnectedRemove(IEntityWithRelationships entity); 

        internal void Add(IEntityWithRelationships entity, bool applyConstraints) 
        {
            // Verify that the entity and owner are in a valid state before we try to do anything with them
            EntityUtil.ValidateRelationshipManager(entity);
            EntityUtil.ValidateRelationshipManager(_owner); 

            // SQLBU: 508819 508813 508752 
            // Detect as soon as possible if we are trying to readd entities which are in Deleted state. 
            // When one of the entity is in Deleted state, attempt would be made to readd this entity
            // to the OSM which is not allowed. 
            // NOTE: Current cleaning code (which uses cleanupOwnerEntity and cleanupPassedInEntity)
            // works only if one of the entity is not attached to the context.
            // PERFORMANCE: following can be performed faster if ObjectStateManager provide method to
            // lookup only in dictionary with Deleted entities (because here we are interestede only in Deleted entities) 
            if (_context != null && !UsingNoTracking)
            { 
                ValidateStateForAdd(_owner); 
                ValidateStateForAdd(entity);
            } 

            this.Add(entity,
                applyConstraints,
                false /*addRelationshipAsUnchanged*/, 
                false /*relationshipAlreadyExists*/);
 
        } 

        internal void CheckRelationEntitySet(EntitySet set) 
        {
            Debug.Assert(set != null, "null EntitySet");
            Debug.Assert(_relationshipSet != null,
                "Should only be checking the RelationshipSet on an attached entity and it should always be non-null in that case"); 

            if ((((AssociationSet)_relationshipSet).AssociationSetEnds[_navigation.To] != null) && 
                (((AssociationSet)_relationshipSet).AssociationSetEnds[_navigation.To].EntitySet != set)) 
            {
                throw EntityUtil.EntitySetIsNotValidForRelationship(set.EntityContainer.Name, set.Name, _navigation.To, _relationshipSet.EntityContainer.Name, _relationshipSet.Name); 
            }
        }

        internal void ValidateStateForAdd(IEntityWithRelationships entity) 
        {
            ObjectStateEntry entry = this.ObjectContext.ObjectStateManager.FindObjectStateEntry(entity); 
            if (entry != null && entry.State == EntityState.Deleted) 
            {
                throw EntityUtil.ObjectStateManagerDoesnotAllowToReAddUnchangedOrModifiedOrDeletedEntity(EntityState.Deleted); 
            }
        }

        internal void Add(IEntityWithRelationships targetEntity, bool applyConstraints, bool addRelationshipAsUnchanged, bool relationshipAlreadyExists) 
        {
            // Do verification 
            if (!this.VerifyEntityForAdd(targetEntity, relationshipAlreadyExists)) 
            {
                // Allow the same item to be "added" to a collection as a no-op operation 
                return;
            }

            EntityKey key = ObjectStateManager.FindKeyOnEntityWithRelationships(targetEntity); 
            if ((object)key != null && ObjectContext != null)
            { 
                CheckRelationEntitySet(key.GetEntitySet(ObjectContext.MetadataWorkspace)); 
            }
 
            RelatedEnd targetRelatedEnd = GetOtherEndOfRelationship(targetEntity);

            if (Object.ReferenceEquals(this.ObjectContext, targetRelatedEnd.ObjectContext) && this.ObjectContext != null)
            { 
                // Both entities are associated with the same non-null context
 
                // Make sure that they are either both tracked or both not tracked, or both don't have contexts 
                if (UsingNoTracking != targetRelatedEnd.UsingNoTracking)
                { 
                    throw EntityUtil.CannotCreateRelationshipBetweenTrackedAndNoTrackedEntities(UsingNoTracking ?
                        this._navigation.From : this._navigation.To);
                }
            } 
            else if(this.ObjectContext != null && targetRelatedEnd.ObjectContext != null)
            { 
                // Both entities have a context 
                if (UsingNoTracking && targetRelatedEnd.UsingNoTracking)
                { 
                    // Both entities are NoTracking, but have different contexts
                    // Attach the owner's context to the target's RelationshipManager
                    // O-C mappings are 1:1, so this operation is allowed
                    targetEntity.RelationshipManager.ResetContext(this.ObjectContext, GetTargetEntitySetFromRelationshipSet(), MergeOption.NoTracking); 
                }
                else 
                { 
                    // Both entities are already tracked by different non-null contexts
                    throw EntityUtil.CannotCreateRelationshipEntitiesInDifferentContexts(); 
                }
            }

            targetRelatedEnd.VerifyEntityForAdd(this.Owner, relationshipAlreadyExists); 

            // Do actual add 
 
            // Add the target entity to the source entity's collection or reference
            this.AddEntityToLocallyCachedCollection(targetEntity, applyConstraints); 

            // Fix up the target end of the relationship by adding the source entity to the target entity's collection or reference
            // devnote: applyConstraints should always be false in this fixup scenario,
            //          and should not just use the same value that was passed into this method. 
            targetRelatedEnd.AddEntityToLocallyCachedCollection(this.Owner, /*applyConstraints*/ false);
            // delay event firing for targetRelatedEnd. once we fire the event, we should be at operation completed state 
 
            // Ensure that both entities end up in the same context:
            // (1) If neither entity is attached to a context, we don't need to do anything else. 
            // (2) If they are both in the same one, we need to make sure neither one was created with MergeOption.NoTracking,
            //     and if not, add a relationship entry if it doesn't already exist.
            // (3) If both entities are already in different contexts, fail.
            // (4) Otherwise, only one entity is attached, and that is the context we will use. 
            //     For the entity that is not attached, attach it to that context.
 
            RelatedEnd attachedRelatedEnd = null; // the end of the relationship that is already attached to a context, if there is one. 
            IEntityWithRelationships entityToAdd = null; // the entity to be added to attachedRelatedEnd
            HashSet promotedEntityKeyRefs = new HashSet(); 

            if (Object.ReferenceEquals(this.ObjectContext, targetRelatedEnd.ObjectContext) && this.ObjectContext != null)
            {
                // Both entities are associated with the same non-null context 

                // Make sure that a relationship entry exists between these two entities. It is possible that the entities could 
                // have been added to the context independently of each other, so the relationship may not exist yet. 
                if (!relationshipAlreadyExists && !UsingNoTracking)
                { 
                    AddRelationshipToObjectStateManager(targetEntity, addRelationshipAsUnchanged, /*doAttach*/false);
                }
            }
            else if (this.ObjectContext != null || targetRelatedEnd.ObjectContext != null) 
            {
                // Only one entity has a context, so figure out which one it is, and determine which entity we will be adding to it 
                if (this.ObjectContext == null) 
                {
                    attachedRelatedEnd = targetRelatedEnd; 
                    entityToAdd = this.Owner;
                }
                else
                { 
                    attachedRelatedEnd = this;
                    entityToAdd = targetEntity; 
                } 

                try 
                {
                    if (!attachedRelatedEnd.UsingNoTracking)
                    {
                        attachedRelatedEnd.AddGraphToObjectStateManager(entityToAdd, relationshipAlreadyExists, 
                            addRelationshipAsUnchanged, /*doAttach*/ false, promotedEntityKeyRefs);
                    } 
 
                    // Reset so we know we successfully added the graph and don't have to clean anything up
                    attachedRelatedEnd = null; 
                    entityToAdd = null;
                }
                finally
                { 
                    // if attachedRelatedEnd is still set, that means we failed during the add to the state manager, and need to clean up
                    if (attachedRelatedEnd != null) 
                    { 
                        Debug.Assert(entityToAdd != null, "entityToAdd should be set if attachedRelatedEnd is set");
 
                        // Remove the source entity from the target related end
                        attachedRelatedEnd.FixupOtherEndOfRelationshipForRemove(entityToAdd);

                        // Remove the target entity from the source related end 
                        attachedRelatedEnd.RemoveEntityFromLocallyCachedCollection(entityToAdd, /*resetIsLoaded*/ false);
 
                        // Remove the graph that we just tried to add to the context 
                        RelationshipManager.RemoveRelatedEntitiesFromObjectStateManager(entityToAdd, promotedEntityKeyRefs);
                        Debug.Assert(promotedEntityKeyRefs.Count == 0, "Haven't cleaned up all of the promoted reference EntityKeys"); 
                        RemoveEntityFromObjectStateManager(entityToAdd);
                    }
                }
            } 

            // else neither entity is associated with a context, so there is no state manager to update 
            // fire the Association changed event, first on targetRelatedEnd then on this EC 
            targetRelatedEnd.OnAssociationChanged(CollectionChangeAction.Add, this.Owner);
            OnAssociationChanged(CollectionChangeAction.Add, targetEntity); 
        }

        private void AddGraphToObjectStateManager(IEntityWithRelationships entity, bool relationshipAlreadyExists,
            bool addRelationshipAsUnchanged, bool doAttach, HashSet promotedEntityKeyRefs) 
        {
            Debug.Assert(!UsingNoTracking, "Should not be attempting to add graphs to the state manager with NoTracking related ends"); 
 
            AddEntityToObjectStateManager(entity, doAttach);
            if (!relationshipAlreadyExists) 
            {
                AddRelationshipToObjectStateManager(entity, addRelationshipAsUnchanged, doAttach);
            }
            WalkObjectGraphToIncludeAllRelatedEntities(entity, addRelationshipAsUnchanged, doAttach, promotedEntityKeyRefs); 
        }
 
        internal void Remove(IEntityWithRelationships entity, bool doFixup, bool deleteEntity, bool deleteOwner, bool applyReferentialConstraints) 
        {
            // Verify that the entity and owner are in a valid state before we try to do anything with them 
            EntityUtil.ValidateRelationshipManager(entity);
            EntityUtil.ValidateRelationshipManager(_owner);

            if (!this.ContainsEntity(entity)) 
            {
                return; 
            } 

            // There can be a case when symmetrical Remove() shall be performed because of Referential Constraints 
            // Example:
            //   Relationshipo Client -> Order with Referential Constraint on in.
            //   When user calls (pseudo code) Order.Remove(Client), we perform Client.Remove(Order),
            //   because removing relationship between Client and Order should cause cascade delete on the Order side. 
            if (null != _context && doFixup &&
                applyReferentialConstraints && 
                IsDependentEndOfReferentialConstraint()) 
            {
                // Remove _owner from the related end with applying Referential Constraints 
                RelatedEnd relatedEnd = GetOtherEndOfRelationship(entity);
                relatedEnd.Remove(_owner, doFixup, deleteEntity, deleteOwner, applyReferentialConstraints);

                return; 
            }
 
 
            //The following call will verify that the given entity is part of the collection or ref.
            bool fireEvent = RemoveEntityFromLocallyCachedCollection(entity, false); 

            if (!UsingNoTracking)
            {
                MarkRelationshipAsDeletedInObjectStateManager(entity, _owner, _relationshipSet, _navigation); 
            }
 
            if (doFixup) 
            {
                bool deleteRelatedEntity = false; 

                //The related end "entity" cannot live without this side "owner". It should be deleted. Cascade this
                // effect to related enteties of the "related" entity
                if (null != _context && (deleteEntity || 
                    (deleteOwner && CheckCascadeDeleteFlag(_fromEndProperty)) ||
                    (applyReferentialConstraints && IsPrincipalEndOfReferentialConstraint()))) 
                { 
                    //Once related entity is deleted, all relationships involving related entity would be updated
                    deleteRelatedEntity = true; 
                }

                // RemoveEntityFromRelatedEnds check for graph circularities to make sure
                // it does not get into infinite loop 
                if (deleteRelatedEntity)
                { 
                    RemoveEntityFromRelatedEnds(entity, _owner, _navigation.Reverse); 
                }
 
                FixupOtherEndOfRelationshipForRemove(entity);

                if (deleteRelatedEntity)
                { 
                    MarkEntityAsDeletedInObjectStateManager(entity);
                } 
            } 

            if (fireEvent) 
            {
                OnAssociationChanged(CollectionChangeAction.Remove, entity);
            }
        } 

        ///  
        /// Check if current RelatedEnd is a Dependent end of some Referential Constraint 
        /// 
        internal bool IsDependentEndOfReferentialConstraint() 
        {
            if (null != _relationMetadata)
            {
                // NOTE Referential constraints collection will usually contains 0 or 1 element, 
                // so performance shouldn't be an issue here
                foreach (ReferentialConstraint constraint in ((AssociationType)_relationMetadata).ReferentialConstraints) 
                { 
                    if (constraint.ToRole == this._fromEndProperty)
                    { 
                        // Example:
                        //    Client --- Order
                        //    RI Constraint: Principal/From ,  Dependent/To 
                        // When current RelatedEnd is a CollectionOrReference in Order's relationships, 
                        // constarint.ToRole == this._fromEndProperty == Order
                        return true; 
                    } 
                }
            } 
            return false;
        }

        ///  
        /// Check if current RelatedEnd is a Principal end of some Referential Constraint
        ///  
        internal bool IsPrincipalEndOfReferentialConstraint() 
        {
            if (null != _relationMetadata) 
            {
                // NOTE Referential constraints collection will usually contains 0 or 1 element,
                // so performance shouldn't be an issue here
                foreach (ReferentialConstraint constraint in ((AssociationType)_relationMetadata).ReferentialConstraints) 
                {
                    if (constraint.FromRole == this._fromEndProperty) 
                    { 
                        // Example:
                        //    Client --- Order 
                        //    RI Constraint: Principal/From ,  Dependent/To 
                        // When current RelatedEnd is a CollectionOrReference in Client's relationships,
                        // constarint.FromRole == this._fromEndProperty == Client
                        return true; 
                    }
                } 
            } 
            return false;
        } 

        //Add given entity and its relationship to ObjectStateManager. Walk graph to recursively
        // add all entities in the graph.
        // If doAttach==TRUE, the entities are attached directly as Unchanged without calling AcceptChanges() 
        internal void IncludeEntity(U entity, bool addRelationshipAsUnchanged, bool doAttach, HashSet promotedEntityKeyRefs)
            where U : class, IEntityWithRelationships 
        { 
            Debug.Assert(!UsingNoTracking, "Should not be trying to include entities in the state manager for NoTracking related ends");
 
            //check to see if entity is already added to the cache
            //search by object reference so that we will not find any entries with the same key but a different object instance
            // NOTE: if (cacheEntry.Entity == entity) then this part of the graph is skipped
            ObjectStateEntry cacheEntry = _context.ObjectStateManager.FindObjectStateEntry(entity); 
            Debug.Assert(cacheEntry == null || cacheEntry.Entity == entity,
                    "Expected to have looked up this state entry by reference, how did we get a different entity?"); 
 
            if (null == cacheEntry ||
                cacheEntry.State == EntityState.Deleted) 
            {
                // NOTE (Attach): if (null == entity.Key) then check must be performed whether entity really
                // doesn't exist in the context (by creating fake Key and calling FindObjectStateEntry(Key) )
                // This is done in the ObjectContext::AttachSingleObject(). 

                AddGraphToObjectStateManager(entity, /*relationshipAlreadyExists*/ false, 
                    addRelationshipAsUnchanged, doAttach, promotedEntityKeyRefs); 
            }
 
            // There is a possibility that related entity is added to cache but relationship is not added.
            // Example: Suppose A and B are related. When walking the graph it is possible that
            // node B was visited through some relationship other than A-B.
            else if (null == FindRelationshipEntryInObjectStateManager(entity)) 
            {
                // If we have a reference with a detached key, make sure the key matches the relationship we are about to add 
                EntityReference entityRef = this as EntityReference; 
                if (entityRef != null && entityRef.DetachedEntityKey != null)
                { 
                    EntityKey targetKey = _context.ObjectStateManager.GetEntityKey(entity);
                    if (entityRef.DetachedEntityKey != targetKey)
                    {
                        throw EntityUtil.EntityKeyValueMismatch(); 
                    }
                    // else -- null just means the key isn't set, so the target entity key doesn't also have to be null 
                } 

                AddRelationshipToObjectStateManager(entity, addRelationshipAsUnchanged, doAttach); 
            }
            // else relationship is already there, nothing more to do
        }
 
        // Remove given entity and its relationship from ObjectStateManager.
        // Traversegraph to recursively remove all entities in the graph. 
        internal void ExcludeEntity(U entity, HashSet promotedEntityKeyRefs) 
            where U : class, IEntityWithRelationships
        { 
            Debug.Assert(!UsingNoTracking, "Should not try to exclude entities from the state manager for NoTracking related ends.");
            //check to see if entity is already removed from the cache
            ObjectStateEntry cacheEntry = _context.ObjectStateManager.FindObjectStateEntry(entity);
 
            if (null != cacheEntry && cacheEntry.State != EntityState.Deleted && !entity.RelationshipManager.NodeVisited)
            { 
                entity.RelationshipManager.NodeVisited = true; 

                RelationshipManager.RemoveRelatedEntitiesFromObjectStateManager(entity, promotedEntityKeyRefs); 
                RemoveRelationshipFromObjectStateManager(entity, _owner, _relationshipSet, _navigation);
                RemoveEntityFromObjectStateManager(entity);
            }
            // There is a possibility that related entity is removed from cache but relationship is not removed. 
            // Example: Suppose A and B are related. When walking the graph it is possible that
            // node B was visited through some relationship other than A-B. 
            else if (null != FindRelationshipEntryInObjectStateManager(entity)) 
            {
                RemoveRelationshipFromObjectStateManager(entity, _owner, _relationshipSet, _navigation); 
            }
        }

        internal ObjectStateEntry FindRelationshipEntryInObjectStateManager(IEntityWithRelationships entity) 
        {
            Debug.Assert(!UsingNoTracking, "Should not look for RelationshipEntry in ObjectStateManager for NoTracking cases."); 
            EntityKey entityKey = ObjectContext.FindEntityKey(entity, this._context); 
            EntityKey ownerKey = ObjectContext.FindEntityKey(_owner, this._context);
            return this._context.ObjectStateManager.FindRelationship(_relationshipSet, 
                new KeyValuePair(_navigation.From, ownerKey),
                new KeyValuePair(_navigation.To, entityKey));
        }
 
        internal void Clear(IEntityWithRelationships entity, RelationshipNavigation navigation, bool doCascadeDelete)
        { 
            ClearCollectionOrRef(entity, navigation, doCascadeDelete); 
        }
 
        // Check if related entities contain proper property values
        // (entities with temporary keys are skiped)
        internal bool CheckReferentialConstraintProperties(EntityKey ownerKey)
        { 
            // if the related end contains a real entity or is a reference with a detached entitykey, we need to check for RI constraints
            if (!this.IsEmpty() || 
                ((ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne || 
                ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One) &&
                ((EntityReference)this).DetachedEntityKey != null)) 
            {
                foreach (ReferentialConstraint constraint in ((AssociationType)this.RelationMetadata).ReferentialConstraints)
                {
                    // Check properties in principals 
                    if (constraint.ToRole == FromEndProperty)
                    { 
                        if (!this.IsEmpty()) 
                        {
                            foreach (IEntityWithRelationships relatedEntity in this) 
                            {
                                EntitySet principalEntitySet = GetTargetEntitySetFromRelationshipSet();
                                // get or create a key to use to compare the values -- the target entity might not have been attached
                                // yet so it may not have a key, but we can create one here to use for checking the values 
                                EntityKey principalKey = ObjectContext.FindEntityKey(relatedEntity, this.ObjectContext);
                                if (null != (object)principalKey) 
                                { 
                                    // Validate the key here because we need to get values from it for verification
                                    // and that will fail if the key is malformed. 
                                    // Verify only if the key already exists.
                                    EntityUtil.ValidateEntitySetInKey(principalKey, principalEntitySet);
                                    principalKey.ValidateEntityKey(principalEntitySet);
                                } 
                                else
                                { 
                                    principalKey = _context.CreateEntityKey(principalEntitySet, relatedEntity); 
                                }
 
                                if (!VerifyRIConstraintsWithRelatedEntry(constraint, ownerKey, principalKey))
                                {
                                    return false;
                                } 
                                // else keep processing the other constraints
                            } 
                        } 
                        else
                        { 
                            // related end is empty, so we must have a reference with a detached key
                            EntityKey detachedKey = ((EntityReference)this).DetachedEntityKey;

                            // don't need to validate the principal/detached key here because that has already been done during AttachContext 
                            if (!VerifyRIConstraintsWithRelatedEntry(constraint, ownerKey, detachedKey))
                            { 
                                return false; 
                            }
                            // else keep processing the other constraints 
                        }
                    }
                    else
                    // Check properties in dependent (only if the related end is an empty EntityReference with EntityKey) 
                    if (constraint.FromRole == FromEndProperty && this.IsEmpty())
                    { 
                        // related end is empty, so we must have a reference with a detached key 
                        EntityKey detachedKey = ((EntityReference)this).DetachedEntityKey;
 
                        // don't need to validate the principal/detached key here because that has already been done during AttachContext
                        if (!VerifyRIConstraintsWithRelatedEntry(constraint, detachedKey, ownerKey))
                        {
                            return false; 
                        }
                        // else keep processing the other constraints 
                    } 
                }
            } 
            return true;
        }

        private static bool VerifyRIConstraintsWithRelatedEntry(ReferentialConstraint constraint, EntityKey dependentKey, EntityKey principalKey) 
        {
            Debug.Assert(constraint.FromProperties.Count == constraint.ToProperties.Count, "RIC: Referential constraints From/To properties list have different size"); 
 
            // NOTE order of properties in collections (From/ToProperties) is important.
            for (int i = 0; i < constraint.FromProperties.Count; ++i) 
            {
                string fromPropertyName = constraint.FromProperties[i].Name;
                string toPropertyName = constraint.ToProperties[i].Name;
 
                object currentValue = principalKey.FindValueByName(fromPropertyName);
                object expectedValue = dependentKey.FindValueByName(toPropertyName); 
 
                Debug.Assert(currentValue != null, "currentValue is part of Key on an attached entity, it must not be null");
 
                if (!currentValue.Equals(expectedValue))
                {
                    // RI Constraint violated
                    return false; 
                }
            } 
 
            return true;
        } 

        public IEnumerator GetEnumerator()
        {
            // Due to the way the CLR handles IEnumerator return types, the check for a null owner for EntityReferences 
            // must be made here because GetInternalEnumerator is delay-executed and so will not throw until the
            // enumerator is advanced 
            if (this is EntityReference) 
            {
                CheckOwnerNull(); 
            }
            return GetInternalEnumerator();
        }
 
        internal void RemoveAll()
        { 
            //copy into list because changing collection member is not allowed during enumeration. 
            // If possible avoid copying into list.
            List deletedEntities = null; 

            bool fireEvent = false;
            try
            { 
                _suppressEvents = true;
                foreach (IEntityWithRelationships e in this) 
                { 
                    if (null == deletedEntities)
                    { 
                        deletedEntities = new List();
                    }
                    deletedEntities.Add(e);
                } 

 
                if (fireEvent = (null != deletedEntities) && (deletedEntities.Count > 0)) 
                {
                    foreach (IEntityWithRelationships e in deletedEntities) 
                    {
                        Remove(e, /*fixup*/true, /*deleteEntity*/false, /*deleteOwner*/true, /*applyReferentialConstraints*/true);
                    }
                } 
            }
            finally 
            { 
                _suppressEvents = false;
            } 
            if (fireEvent)
            {
                OnAssociationChanged(CollectionChangeAction.Refresh, null);
            } 
        }
 
        internal void DetachAll(EntityState ownerEntityState) 
        {
            //copy into list because changing collection member is not allowed during enumeration. 
            // If possible avoid copying into list.
            List deletedEntities = new List();

            foreach (IEntityWithRelationships e in this) 
            {
                deletedEntities.Add(e); 
            } 

            bool detachRelationship = 
                    ownerEntityState == EntityState.Added ||
                    _fromEndProperty.RelationshipMultiplicity == RelationshipMultiplicity.Many;

            // every-fix up will fire with Remove action 
            // every forward operation (removing from this relatedEnd) will fire with Refresh
            // do not merge the loops, handle the related ends seperately (when the event is being fired, 
            // we should be in good state: for every entity deleted, related event should have been fired) 
            foreach (IEntityWithRelationships entity in deletedEntities)
            { 
                // future enhancement: it does not make sense to return in the half way, either remove this code or
                // move it to the right place
                if (!this.ContainsEntity(entity))
                { 
                    return;
                } 
 
                // if this is a reference, set the EntityKey property before removing the relationship and entity
                EntityReference entityReference = this as EntityReference; 
                if (entityReference != null)
                {
                    entityReference.DetachedEntityKey = entityReference.AttachedEntityKey;
                } 

                if (detachRelationship) 
                { 
                    DetachRelationshipFromObjectStateManager(entity, _owner, _relationshipSet, _navigation);
                } 
                RelatedEnd relatedEnd = GetOtherEndOfRelationship(entity);
                relatedEnd.RemoveEntityFromLocallyCachedCollection(_owner, /* resetIsLoaded */ true);
                relatedEnd.OnAssociationChanged(CollectionChangeAction.Remove, _owner);
            } 
            foreach (IEntityWithRelationships entity in deletedEntities)
            { 
                RelatedEnd relatedEnd = GetOtherEndOfRelationship(entity); 
                this.RemoveEntityFromLocallyCachedCollection(entity, /* resetIsLoaded */ false);
            } 
            this.OnAssociationChanged(CollectionChangeAction.Refresh, null);

            Debug.Assert(this.IsEmpty(), "Collection or reference should be empty");
        } 

        internal abstract bool VerifyEntityForAdd(IEntityWithRelationships entity, bool relationshipAlreadyExists); 
        internal abstract void AddEntityToLocallyCachedCollection(IEntityWithRelationships entity, bool applyConstraints); 
        internal abstract bool RemoveEntityFromLocallyCachedCollection(IEntityWithRelationships entity, bool resetIsLoaded);
        internal abstract void Include(bool addRelationshipAsUnchanged, bool doAttach, HashSet promotedEntityKeyRefs); 
        internal abstract void Exclude(HashSet promotedEntityKeyRefs);
        internal abstract void ClearCollectionOrRef(IEntityWithRelationships entity, RelationshipNavigation navigation, bool doCascadeDelete);
        internal abstract bool ContainsEntity(IEntityWithRelationships entity);
        internal abstract IEnumerator GetInternalEnumerator(); 
        internal abstract void RetrieveReferentialConstraintProperties(Dictionary> keyValues, HashSet visited);
        internal abstract bool IsEmpty(); 
        internal abstract void OnRelatedEndClear(); 

        internal virtual void OnAssociationChanged(CollectionChangeAction collectionChangeAction, object entity) 
        {
            if (!_suppressEvents)
            {
                if (_onAssociationChanged != null) 
                {
                    _onAssociationChanged(this, (new CollectionChangeEventArgs(collectionChangeAction, entity))); 
                } 
            }
        } 

        private void AddEntityToObjectStateManager(IEntityWithRelationships entity, bool doAttach)
        {
            Debug.Assert(_context != null, "Can't add to state manager if _context is null"); 
            Debug.Assert(!UsingNoTracking, "Should not add an Entity to ObjectStateManager for NoTracking cases.");
 
#if DEBUG 
            // we should have already verified that this entity does not exist at all, or that it's Deleted
            ObjectStateEntry cacheEntry = _context.ObjectStateManager.FindObjectStateEntry(entity); 
            Debug.Assert(cacheEntry == null || cacheEntry.State == EntityState.Deleted,
                "Expected to have looked up this state entry by reference, how did we get a different entity?");
#endif
 
            EntitySet es = GetTargetEntitySetFromRelationshipSet();
            if (!doAttach) 
            { 
                _context.AddSingleObject(es, entity, "entity");
            } 
            else
            {
                _context.AttachSingleObject(entity, es, "entity");
            } 

            // Now that we know we have a valid EntityKey for the target entity, verify that it matches the detached EntityKey, if there is one 
            EntityReference entityRef = this as EntityReference; 
            if (entityRef != null && entityRef.DetachedEntityKey != null)
            { 
                EntityKey targetKey = _context.ObjectStateManager.GetEntityKey(entity);
                if (entityRef.DetachedEntityKey != targetKey)
                {
                    throw EntityUtil.EntityKeyValueMismatch(); 
                }
                // else -- null just means the key isn't set, so the target entity key doesn't also have to be null 
            } 
        }
 
        internal EntitySet GetTargetEntitySetFromRelationshipSet()
        {
            EntitySet entitySet = null;
            AssociationSet associationSet = (AssociationSet)_relationshipSet; 
            Debug.Assert(associationSet != null, "(AssociationSet) cast failed");
 
            AssociationEndMember associationEndMember = (AssociationEndMember)ToEndMember; 
            Debug.Assert(associationEndMember != null, "(AssociationEndMember) cast failed");
 
            entitySet = associationSet.AssociationSetEnds[associationEndMember.Name].EntitySet;
            Debug.Assert(entitySet != null, "cannot find entitySet");
            return entitySet;
        } 

        private void AddRelationshipToObjectStateManager(IEntityWithRelationships entity, bool addRelationshipAsUnchanged, bool doAttach) 
        { 
            Debug.Assert(!UsingNoTracking, "Should not add Relationship to ObjectStateManager for NoTracking cases.");
            if (this._context != null && entity.RelationshipManager.Context != null) 
            {
                EntityKey ownerKey = this._context.ObjectStateManager.GetEntityKey(_owner);
                EntityKey entityKey = this._context.ObjectStateManager.GetEntityKey(entity);
 
                _context.ObjectStateManager.AddRelation(
                    new RelationshipWrapper((AssociationSet)_relationshipSet, 
                           new KeyValuePair(_navigation.From, ownerKey), 
                           new KeyValuePair(_navigation.To, entityKey)),
                    // When Add method is called through Load API the relationship cache entries 
                    // needs to be added to ObjectStateManager in Unchanged state rather then Added state
                    (addRelationshipAsUnchanged || doAttach) ? EntityState.Unchanged : EntityState.Added);
            }
        } 

        private static void WalkObjectGraphToIncludeAllRelatedEntities(IEntityWithRelationships entity, 
            bool addRelationshipAsUnchanged, bool doAttach, HashSet promotedEntityKeyRefs) 
        {
            foreach (RelatedEnd relatedEnd in entity.RelationshipManager.Relationships) 
            {
                relatedEnd.Include(addRelationshipAsUnchanged, doAttach, promotedEntityKeyRefs);
            }
        } 

        internal static void RemoveEntityFromObjectStateManager(IEntityWithRelationships entity) 
        { 
            ObjectStateEntry entry;
 
            if (entity.RelationshipManager.Context != null &&
                entity.RelationshipManager.Context.ObjectStateManager.IsAttachTracking &&
                entity.RelationshipManager.Context.ObjectStateManager.PromotedKeyEntries.TryGetValue(entity, out entry))
            { 
                // This is executed only in the cleanup code from ObjectContext.AttachTo()
                // If the entry was promoted in AttachTo(), it has to be degraded now instead of being deleted. 
                entry.DegradeEntry(); 
            }
            else 
            {
                entry = MarkEntityAsDeletedInObjectStateManager(entity);
                if (entry != null && entry.State != EntityState.Detached)
                { 
                    entry.AcceptChanges();
                } 
            } 
        }
 
        private static void RemoveRelationshipFromObjectStateManager(IEntityWithRelationships entity, IEntityWithRelationships owner, RelationshipSet relationshipSet, RelationshipNavigation navigation)
        {
            ObjectStateEntry deletedEntry = MarkRelationshipAsDeletedInObjectStateManager(entity, owner, relationshipSet, navigation);
            if (deletedEntry != null && deletedEntry.State != EntityState.Detached) 
            {
                deletedEntry.AcceptChanges(); 
            } 
        }
 
        private void FixupOtherEndOfRelationshipForRemove(IEntityWithRelationships entity)
        {
            RelatedEnd relatedEnd = GetOtherEndOfRelationship(entity);
            relatedEnd.Remove(_owner, /*fixup*/false, /*deleteEntity*/false, /*deleteOwner*/false, /*applyReferentialConstraints*/false); 
        }
 
        private static ObjectStateEntry MarkEntityAsDeletedInObjectStateManager(IEntityWithRelationships entity) 
        {
            ObjectStateEntry entry = null; 
            if (entity.RelationshipManager.Context != null)
            {
                entry = entity.RelationshipManager.Context.ObjectStateManager.FindObjectStateEntry(entity);
 
                if (entry != null)
                { 
                    entry.Delete(/*doFixup*/false); 
                }
            } 
            return entry;
        }

        private static ObjectStateEntry MarkRelationshipAsDeletedInObjectStateManager(IEntityWithRelationships entity, IEntityWithRelationships owner, RelationshipSet relationshipSet, RelationshipNavigation navigation) 
        {
            ObjectStateEntry entry = null; 
            if (owner.RelationshipManager.Context != null && entity.RelationshipManager.Context != null && relationshipSet != null) 
            {
                EntityKey ownerKey = owner.RelationshipManager.Context.ObjectStateManager.GetEntityKey(owner); 
                EntityKey entityKey = entity.RelationshipManager.Context.ObjectStateManager.GetEntityKey(entity);

                entry = entity.RelationshipManager.Context.ObjectStateManager.DeleteRelationship(relationshipSet,
                                      new KeyValuePair(navigation.From, ownerKey), 
                                      new KeyValuePair(navigation.To, entityKey));
            } 
            return entry; 
        }
 
        private static void DetachRelationshipFromObjectStateManager(IEntityWithRelationships entity, IEntityWithRelationships owner, RelationshipSet relationshipSet, RelationshipNavigation navigation)
        {
            if (owner.RelationshipManager.Context != null && entity.RelationshipManager.Context != null && relationshipSet != null)
            { 
                EntityKey ownerKey = owner.RelationshipManager.Context.ObjectStateManager.GetEntityKey(owner);
                EntityKey entityKey = entity.RelationshipManager.Context.ObjectStateManager.GetEntityKey(entity); 
                ObjectStateEntry entry = entity.RelationshipManager.Context.ObjectStateManager.FindRelationship(relationshipSet, 
                                                                          new KeyValuePair(navigation.From, ownerKey),
                                                                          new KeyValuePair(navigation.To, entityKey)); 
                if (entry != null)
                {
                    entry.DetachRelationshipEntry();
                } 
            }
        } 
 
        private static void RemoveEntityFromRelatedEnds(IEntityWithRelationships entity1, IEntityWithRelationships entity2, RelationshipNavigation navigation)
        { 
            foreach (RelatedEnd relatedEnd in entity1.RelationshipManager.Relationships)
            {
                bool doCascadeDelete = false;
                //check for cascade delete flag 
                doCascadeDelete = CheckCascadeDeleteFlag(relatedEnd.FromEndProperty) || relatedEnd.IsPrincipalEndOfReferentialConstraint();
                //Remove the owner from the related end 
                relatedEnd.Clear(entity2, navigation, doCascadeDelete); 
            }
        } 

        private static bool CheckCascadeDeleteFlag(RelationshipEndMember relationEndProperty)
        {
            if (null != relationEndProperty) 
            {
                return (relationEndProperty.DeleteBehavior == OperationAction.Cascade); 
            } 
            return false;
        } 


        internal void AttachContext(ObjectContext context, MergeOption mergeOption)
        { 
            EntityUtil.CheckKeyForRelationship(_owner, mergeOption);
            EntityKey ownerKey = ObjectContext.FindEntityKey(_owner, context); 
            EntityUtil.CheckEntityKeyNull(ownerKey); 
            EntitySet entitySet = ownerKey.GetEntitySet(context.MetadataWorkspace);
 
            AttachContext(context, entitySet, mergeOption);
        }

 
        /// 
        /// Set the context and load options so that Query can be 
        /// constructed on demand. 
        /// 
        internal void AttachContext(ObjectContext context, EntitySet entitySet, MergeOption mergeOption) 
        {
            EntityUtil.CheckArgumentNull(context, "context");
            EntityUtil.CheckArgumentMergeOption(mergeOption);
            EntityUtil.CheckArgumentNull(entitySet, "entitySet"); 

            ((IEntityWithRelationships)_owner).RelationshipManager.NodeVisited = false; 
            // If the context is the same as what we already have, and the mergeOption is consistent with our UsingNoTracking setting, nothing more to do 
            if (_context == context && (_usingNoTracking == (mergeOption == MergeOption.NoTracking)))
            { 
                return;
            }

            bool doCleanup = true; 

            try 
            { 
                // if the source isn't null, clear it
                _sourceQuery = null; 
                this._context = context;
                this._usingNoTracking = (mergeOption == MergeOption.NoTracking);

                // find the relationship set 
                Debug.Assert(_context.MetadataWorkspace != null, "The context should not have a null metadata workspace.");
 
                // find the TypeMetadata for the given relationship 
                EdmType relationshipType = _context.MetadataWorkspace.GetItem(_navigation.RelationshipName, DataSpace.CSpace);
                if (relationshipType == null) 
                {
                    throw EntityUtil.NoRelationshipSetMatched(_navigation.RelationshipName);
                }
 
                // find the RelationshipSet
                RelationshipSet relationshipSet = null; 
                foreach (EntitySetBase entitySetBase in entitySet.EntityContainer.BaseEntitySets) 
                {
                    if ((EdmType)entitySetBase.ElementType == relationshipType) 
                    {
                        if (((AssociationSet)entitySetBase).AssociationSetEnds[_navigation.From].EntitySet == entitySet)
                        {
                            relationshipSet = (RelationshipSet)entitySetBase; 
                            break;
                        } 
                    } 
                }
 
                if (relationshipSet != null)
                {
                    this._relationshipSet = relationshipSet;
                    this._relationMetadata = (RelationshipType)relationshipType; 
                }
                else 
                { 
                    foreach (EntitySetBase set in entitySet.EntityContainer.BaseEntitySets)
                    { 
                        AssociationSet associationset = set as AssociationSet;
                        if (associationset != null)
                        {
                            if (associationset.ElementType == relationshipType && 
                                associationset.AssociationSetEnds[_navigation.From].EntitySet != entitySet &&
                                associationset.AssociationSetEnds[_navigation.From].EntitySet.ElementType == entitySet.ElementType) 
                                throw EntityUtil.EntitySetIsNotValidForRelationship(entitySet.EntityContainer.Name, entitySet.Name, _navigation.From ,((AssociationSet)set).EntityContainer.Name,((AssociationSet)set).Name); 
                        }
                    } 
                    throw EntityUtil.NoRelationshipSetMatched(_navigation.RelationshipName);
                }

                //find relation end property 
                bool foundFromRelationEnd = false;
                bool foundToRelationEnd = false; 
                foreach (AssociationEndMember relationEnd in ((AssociationType)_relationMetadata).AssociationEndMembers) //Only Association relationship is supported 
                {
                    if (relationEnd.Name == this._navigation.From) 
                    {
                        if (foundFromRelationEnd)
                        {
                            throw EntityUtil.FoundMoreThanOneRelatedEnd(); 
                        }
                        foundFromRelationEnd = true; 
                        this._fromEndProperty = relationEnd; 
                    }
                    if (relationEnd.Name == this._navigation.To) 
                    {
                        if (foundToRelationEnd)
                        {
                            throw EntityUtil.FoundMoreThanOneRelatedEnd(); 
                        }
                        foundToRelationEnd = true; 
                        this._toEndProperty = relationEnd; 
                    }
                } 
                if (!(foundFromRelationEnd && foundToRelationEnd))
                {
                    throw EntityUtil.RelatedEndNotFound();
                } 

                // If this is a stub EntityReference and the DetachedEntityKey is set, make sure it is valid 
                if (this.IsEmpty()) 
                {
                    // if there are no contained entities but this is a reference with a detached entity key, validate the key 
                    EntityReference entityRef = this as EntityReference;
                    if (entityRef != null && entityRef.DetachedEntityKey != null)
                    {
                        EntityKey detachedKey = entityRef.DetachedEntityKey; 
                        if (!IsValidEntityKeyType(detachedKey))
                        { 
                            // devnote: We have to check this here instead of in the EntityKey property setter, 
                            //          because the key could be set to an invalid type temporarily during deserialization
                            throw EntityUtil.CannotSetSpecialKeys(); 
                        }
                        EntitySet targetEntitySet = detachedKey.GetEntitySet(context.MetadataWorkspace);
                        CheckRelationEntitySet(targetEntitySet);
                        detachedKey.ValidateEntityKey(targetEntitySet); 
                    }
                } 
                // else even for a reference we don't need to validate the key 
                // because it will be checked later once we have the key for the contained entity
 
                doCleanup = false;
            }
            finally
            { 
                if (doCleanup)
                { 
                    // Uninitialize fields, so the cleanup code (for example in RelationshipWrapper.RemoveRelatedEntitiesFromObjectStateManager) 
                    // knows that this RelatedEnd was not properly Attached.
                    this.DetachContext(); 
                }
            }
        }
 
        /// 
        /// Clear the source and context. 
        ///  
        internal void DetachContext()
        { 
            this._sourceQuery = null;
            this._context = null;
            this._relationshipSet = null;
            this._fromEndProperty = null; 
            this._toEndProperty = null;
 
            // Detached entity should have IsLoaded property set to false 
            this._isLoaded = false;
        } 

        /// 
        ///  This method is very similar to the private method in Query of U class
        ///  Only difference is that it calls meterializer with a overload which does 
        ///  not skip the deleted items.
        ///  
        /// query of U 
        /// 
        internal static IEnumerable GetResults(ObjectQuery query) 
        {
            return query.Execute(query.MergeOption);
        }
 
        internal RelatedEnd GetOtherEndOfRelationship(IEntityWithRelationships entity)
        { 
            return (RelatedEnd)entity.RelationshipManager.GetRelatedEnd(_navigation.Reverse, _relationshipFixer); 
        }
 
        // We have to allow a default constructor for serialization, so we need to make sure that the only
        // thing you can do with a null owner is get/set the EntityReference.EntityKey property. All other
        // operations are invalid. This needs to be used on all public methods in this class and EntityReference
        // but not in EntityCollection because EntityCollection does not have a default constructor. 
        // It is not possible to get an EntityReference with a null Owner into the RelationshipManager, and there
        // is no way to access EntityReference without creating one using the default constructor or going through 
        // the RelationshipManager, so we don't need to check this in internal or private methods. 
        internal void CheckOwnerNull()
        { 
            if (_owner == null)
            {
                throw EntityUtil.OwnerIsNull();
            } 
        }
 
        // This method is intended to be used to support the public API InitializeRelatedReference, where we have to take an existing EntityReference 
        // and set up the appropriate fields as shown below, instead of creating a new EntityReference and setting these fields in the constructor.
        // This is also used by the constructor -- if we add something that needs to be set at construction time, it probably needs to be set for InitializeRelatedReference as well. 
        internal void InitializeRelatedEnd(IEntityWithRelationships owner, RelationshipNavigation navigation, IRelationshipFixer relationshipFixer)
        {
            _owner = owner;
            _navigation = navigation; 
            _relationshipFixer = relationshipFixer;
        } 
 
        internal static bool IsValidEntityKeyType(EntityKey entityKey)
        { 
            return !(entityKey.IsTemporary || Object.ReferenceEquals(EntityKey.EntityNotValidKey, entityKey) || Object.ReferenceEquals(EntityKey.NoEntitySetKey, entityKey));
        }
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner       [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 
using System.Collections;
using System.Collections.Generic; 
using System.ComponentModel;
using System.Data;
using System.Data.Common;
using System.Data.Common.CommandTrees; 
using System.Data.Common.Utils;
using System.Data.Metadata.Edm; 
using System.Data.Objects.Internal; 
using System.Diagnostics;
using System.Text; 
using System.Runtime.Serialization;

namespace System.Data.Objects.DataClasses
{ 
    /// 
    /// Base class for EntityCollection and EntityReference 
    ///  

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] 
    [DataContract]
    [Serializable]
    public abstract class RelatedEnd : IRelatedEnd
    { 

        //----------------- 
        // Internal Constructors 
        //-----------------
 
        /// 
        /// The default constructor is required for some serialization scenarios with EntityReference.
        /// 
        internal RelatedEnd() 
        {
        } 
 
        internal RelatedEnd(IEntityWithRelationships owner, RelationshipNavigation navigation, IRelationshipFixer relationshipFixer)
        { 
            EntityUtil.CheckArgumentNull(owner, "owner");
            EntityUtil.CheckArgumentNull(navigation, "navigation");
            EntityUtil.CheckArgumentNull(relationshipFixer, "fixer");
 
            InitializeRelatedEnd(owner, navigation, relationshipFixer);
        } 
 
        // ------
        // Fields 
        // ------
        private const string _entityKeyParamName = "EntityKeyValue";

        // The following fields are serialized.  Adding or removing a serialized field is considered 
        // a breaking change.  This includes changing the field type or field name of existing
        // serialized fields. If you need to make this kind of change, it may be possible, but it 
        // will require some custom serialization/deserialization code. 
        // These fields should not be changed once they have been initialized with non-null values, but they can't be read-only because there
        // are serialization scenarios where they have to be set after construction 
        private IEntityWithRelationships _owner;
        private RelationshipNavigation _navigation;
        private IRelationshipFixer _relationshipFixer;
 
        internal bool _isLoaded;
 
        // The fields in this group are set only when attached to a context, so we don't need to serialize. 
        [NonSerialized]
        private RelationshipSet _relationshipSet; 
        [NonSerialized]
        private ObjectContext _context;
        [NonSerialized]
        private bool _usingNoTracking; 
        [NonSerialized]
        private RelationshipType _relationMetadata; 
        [NonSerialized] 
        private RelationshipEndMember _fromEndProperty; //owner end property
        [NonSerialized] 
        private RelationshipEndMember _toEndProperty;
        [NonSerialized]
        private string _sourceQuery;
 
        [NonSerialized]
        internal bool _suppressEvents; 
        [NonSerialized] 
        internal CollectionChangeEventHandler _onAssociationChanged;
 

        // ------
        // Events
        // ------ 

        ///  
        /// Event to notify changes in the Associations. 
        /// 
        public event CollectionChangeEventHandler AssociationChanged 
        {
            add
            {
                CheckOwnerNull(); 
                _onAssociationChanged += value;
            } 
            remove 
            {
                CheckOwnerNull(); 
                _onAssociationChanged -= value;
            }
        }
 
        /// internal event to notify change in collection
        internal virtual event CollectionChangeEventHandler AssociationChangedForObjectView 
        { 
            // we fire this event only from EntityCollection, definetely not from EntityReference
            add { Debug.Assert(false, "should never happen"); } 
            remove { Debug.Assert(false, "should never happen"); }
        }

 
        // ----------
        // Properties 
        // ---------- 

        ///  
        /// This class describes a relationship navigation from the
        /// navigation property on one entity to another entity.
        /// RelationshipNavigation uniquely identify a relationship type.
        /// The RelationshipNavigation class is internal only, so this property is also internal. 
        /// See RelationshipName, SourceRoleName, and TargetRoleName for the public exposure
        /// of the information contained in this RelationshipNavigation. 
        ///  
        internal RelationshipNavigation RelationshipNavigation
        { 
            get
            {
                return _navigation;
            } 
        }
 
        ///  
        /// Name of the relationship in which this RelatedEnd is participating
        ///  
        [System.Xml.Serialization.SoapIgnore]
        [System.Xml.Serialization.XmlIgnore]
        public string RelationshipName
        { 
            get
            { 
                CheckOwnerNull(); 
                return _navigation.RelationshipName;
            } 
        }

        /// 
        /// Name of the relationship source role used to generate this RelatedEnd 
        /// 
        [System.Xml.Serialization.SoapIgnore] 
        [System.Xml.Serialization.XmlIgnore] 
        public string SourceRoleName
        { 
            get
            {
                CheckOwnerNull();
                return _navigation.From; 
            }
        } 
 
        /// 
        /// Name of the relationship target role used to generate this RelatedEnd 
        /// 
        [System.Xml.Serialization.SoapIgnore]
        [System.Xml.Serialization.XmlIgnore]
        public string TargetRoleName 
        {
            get 
            { 
                CheckOwnerNull();
                return _navigation.To; 
            }
        }

        IEnumerable IRelatedEnd.CreateSourceQuery() 
        {
            CheckOwnerNull(); 
            return this.CreateSourceQueryInternal(); 
        }
 
        internal IEntityWithRelationships Owner
        {
            get
            { 
                return this._owner;
            } 
        } 

        internal ObjectContext ObjectContext 
        {
            get
            {
                return this._context; 
            }
        } 
 
        internal virtual void BulkDeleteAll(System.Collections.Generic.List list)
        { 
            throw EntityUtil.NotSupported();
        }

        ///  
        /// Returns the relationship metadata associated with this RelatedEnd.
        /// This value is available once the RelatedEnd is attached to an ObjectContext 
        /// or is retrieved with MergeOption.NoTracking 
        /// 
        [System.Xml.Serialization.SoapIgnore] 
        [System.Xml.Serialization.XmlIgnore]
        public RelationshipSet RelationshipSet
        {
            get 
            {
                CheckOwnerNull(); 
                return this._relationshipSet; 
            }
        } 

        internal RelationshipType RelationMetadata
        {
            get 
            {
                return this._relationMetadata; 
            } 
        }
 
        internal RelationshipEndMember ToEndMember
        {
            get
            { 
                return this._toEndProperty;
            } 
        } 

        internal bool UsingNoTracking 
        {
            get
            {
                return this._usingNoTracking; 
            }
        } 
 
        internal MergeOption DefaultMergeOption
        { 
            get
            {
                return UsingNoTracking ? MergeOption.NoTracking : MergeOption.AppendOnly;
            } 
        }
 
        internal RelationshipEndMember FromEndProperty 
        {
            get 
            {
                return this._fromEndProperty;
            }
        } 

        ///  
        /// IsLoaded returns true if and only if Load was called. 
        /// 
        [System.Xml.Serialization.SoapIgnore] 
        [System.Xml.Serialization.XmlIgnore]
        public bool IsLoaded
        {
            get 
            {
                CheckOwnerNull(); 
                return this._isLoaded; 
            }
        } 

        internal void SetIsLoaded(bool value)
        {
            this._isLoaded = value; 
        }
 
        ///  
        /// This is the query which represents the source of the
        /// related end.  It is constructed on demand using the 
        /// _connection and _cache fields and a query string based on
        /// the type of related end and the metadata passed into its
        /// constructor indicating the particular EDM construct the
        /// related end models. This method is called by both subclasses of this type 
        /// and those subclasses pass in their generic type parameter in order
        /// to produce an ObjectQuery of the right type. This allows this common 
        /// functionality to be implemented here in the base class while still 
        /// allowing the base class to be non-generic.
        ///  
        /// MergeOption to use when creating the query
        internal ObjectQuery CreateSourceQuery(MergeOption mergeOption)
        {
            // must have a context 
            if (_context == null)
            { 
                return null; 
            }
 
            ObjectStateEntry stateEntry = _context.ObjectStateManager.FindObjectStateEntry(_owner);
            EntityState entityState;
            if (stateEntry == null)
            { 
                if (UsingNoTracking)
                { 
                    entityState = EntityState.Detached; 
                }
                else 
                {
                    throw EntityUtil.InvalidEntityStateSource();
                }
            } 
            else
            { 
                Debug.Assert(stateEntry != null, "Entity should exist in the current context"); 
                entityState = stateEntry.State;
            } 

            //Throw incase entity is in added state
            if (entityState == EntityState.Added)
            { 
                throw EntityUtil.InvalidEntityStateSource();
            } 
 
            Debug.Assert(!(entityState != EntityState.Detached && UsingNoTracking), "Entity with NoTracking option cannot exist in the ObjectStateManager");
 
            // the CreateSourceQuery method can only return non-NULL when we're
            // either detached & mergeOption is NoTracking or
            // Modified/Unchanged/Deleted and mergeOption is NOT NoTracking
            // (if entity is attached to the context, mergeOption should never be NoTracking) 
            if (!((entityState == EntityState.Detached && UsingNoTracking) ||
                   entityState == EntityState.Modified || 
                   entityState == EntityState.Unchanged || 
                   entityState == EntityState.Deleted))
            { 
                return null;
            }

            // Construct a new source query and return it. 
            Debug.Assert(_relationshipSet != null, "If we are attached to a context, we should have a relationship set.");
 
            // Retrieve the entity key of the owner. 
            EntityKey key = _context.ObjectStateManager.GetEntityKey(_owner);
 
            // If the source query text has not be initialized, then do so now.
            if (null == _sourceQuery)
            {
                // Translate to: 
                // SELECT VALUE [TargetEntity]
                //  FROM 
                //      (SELECT VALUE x FROM ##RelationshipSet AS x 
                //       WHERE Key(x.[##SourceRoleName]) = ROW(@key1 AS key1[..., @keyN AS keyN])
                //       ) AS [AssociationEntry] 
                //  INNER JOIN
                //       OfType(##TargetEntityset, ##TargetRole.EntityType) AS [TargetEntity]
                //  ON
                //       Key([AssociationEntry].##TargetRoleName) = Key(Ref([TargetEntity])) 
                //
                // Note that the OfType operator can be omitted if the element type of ##TargetEntitySet 
                // is equal to the Entity type produced by the target end of the relationship. 
                Debug.Assert(_relationshipSet.BuiltInTypeKind == BuiltInTypeKind.AssociationSet, "Non-AssociationSet Relationship Set?");
                EntitySet targetEntitySet = ((AssociationSet)_relationshipSet).AssociationSetEnds[_toEndProperty.Name].EntitySet; 

                StringBuilder sourceBuilder = new StringBuilder("SELECT VALUE [TargetEntity] FROM (SELECT VALUE x FROM ");
                sourceBuilder.Append("[");
                sourceBuilder.Append(_relationshipSet.EntityContainer.Name); 
                sourceBuilder.Append("].[");
                sourceBuilder.Append(_relationshipSet.Name); 
                sourceBuilder.Append("] AS x WHERE Key(x.["); 
                sourceBuilder.Append(_fromEndProperty.Name);
                sourceBuilder.Append("]) = ROW("); 

                AliasGenerator keyParamNameGen = new AliasGenerator(_entityKeyParamName); // Aliases are cached in AliasGenerator
                int keyMemberCount = key.GetEntitySet(ObjectContext.MetadataWorkspace).ElementType.KeyMembers.Count;
                for(int idx = 0; idx < keyMemberCount; idx++) 
                {
                    string keyParamName = keyParamNameGen.Next(); 
                    sourceBuilder.Append("@"); 
                    sourceBuilder.Append(keyParamName);
                    sourceBuilder.Append(" AS "); 
                    sourceBuilder.Append(keyParamName);

                    if(idx < keyMemberCount - 1)
                    { 
                        sourceBuilder.Append(",");
                    } 
                } 

                sourceBuilder.Append(")) AS [AssociationEntry] INNER JOIN "); 

                EntityType targetEntityType = MetadataHelper.GetEntityTypeForEnd((AssociationEndMember)_toEndProperty);
                bool ofTypeRequired = false;
                if(!targetEntitySet.ElementType.EdmEquals(targetEntityType) && 
                    !TypeSemantics.IsSubTypeOf(targetEntitySet.ElementType, targetEntityType))
                { 
                    // If the type contained in the target entity set is not equal to 
                    // or a subtype of the referenced type, then an OfType must be
                    // applied to the target entityset to yield only those elements that 
                    // are of the referenced type or a subtype of the referenced type.
                    ofTypeRequired = true;

                    // The type name used in the OfType clause must be the name of the 
                    // corresponding O-Space Entity type, since the source query will be
                    // parsed using the CLR perspective (by ObjectQuery). 
                    TypeUsage targetOSpaceTypeUsage = ObjectContext.MetadataWorkspace.GetOSpaceTypeUsage(TypeUsage.Create(targetEntityType)); 
                    targetEntityType = (EntityType)targetOSpaceTypeUsage.EdmType;
                } 

                if(ofTypeRequired)
                {
                    sourceBuilder.Append("OfType("); 
                }
                sourceBuilder.Append("["); 
                sourceBuilder.Append(targetEntitySet.EntityContainer.Name); 
                sourceBuilder.Append("].[");
                sourceBuilder.Append(targetEntitySet.Name); 
                sourceBuilder.Append("]");
                if(ofTypeRequired)
                {
                    sourceBuilder.Append(", ["); 
                    if (targetEntityType.NamespaceName != string.Empty)
                    { 
                        sourceBuilder.Append(targetEntityType.NamespaceName); 
                        sourceBuilder.Append("].[");
                    } 
                    sourceBuilder.Append(targetEntityType.Name);
                    sourceBuilder.Append("])");
                }
                sourceBuilder.Append(" AS [TargetEntity] ON Key([AssociationEntry].["); 
                sourceBuilder.Append(_toEndProperty.Name);
                sourceBuilder.Append("]) = Key(Ref([TargetEntity]))"); 
 
                _sourceQuery = sourceBuilder.ToString();
            } 

            // Create a new ObjectQuery based on the source query text, the object context, and the specfied merge option.
            ObjectQuery query = new ObjectQuery(_sourceQuery, _context, mergeOption);
 
            // Add a parameter for each entity key value found on the key.
            AliasGenerator paramNameGen = new AliasGenerator(_entityKeyParamName); // Aliases are cached in AliasGenerator 
            ReadOnlyMetadataCollection.Enumerator keyMembers = key.GetEntitySet(ObjectContext.MetadataWorkspace).ElementType.KeyMembers.GetEnumerator(); 
            foreach (EntityKeyMember keyValue in key.EntityKeyValues)
            { 
                // Move on to the key member that corresponds to the current key value.
                keyMembers.MoveNext();

                // Create a new ObjectParameter with the next parameter name and the next key value. 
                ObjectParameter queryParam = new ObjectParameter(paramNameGen.Next(), keyValue.Value);
 
                // Map the type of the key member to S-Space and explicitly specify this mapped type 
                // as the effective type of the new ObjectParameter - this is required so that the
                // type of the key value parameter matches the declared type of the key member when 
                // the query text is parsed.
                queryParam.TypeUsage = Helper.GetModelTypeUsage(keyMembers.Current);

                // Add the new parameter to the Parameters collection of the query. 
                query.Parameters.Add(queryParam);
            } 
 
            // It should not be possible to add or remove parameters from the new query, since the query text
            // is fixed. Adding or removing parameters will likely make the query fail to execute. 
            query.Parameters.SetReadOnly(true);

            // Return the new ObjectQuery. Note that this is intentionally a tear-off so that any changes made
            // to its Parameters collection (or the ObjectParameters themselves) have no effect on anyone else 
            // that may retrieve this query - each access will always return a new ObjectQuery instance.
            return query; 
        } 

        ///  
        /// Validates that a call to Load has the correct conditions
        /// This helps to reduce the complexity of the Load call (SQLBU 524128)
        /// 
        /// See RelatedEnd.CreateSourceQuery method. This is returned here so we can create it and validate the state before returning it to the caller 
        protected ObjectQuery ValidateLoad(MergeOption mergeOption, string relatedEndName)
        { 
            ObjectQuery sourceQuery = CreateSourceQuery(mergeOption); 
            if (null == sourceQuery)
            { 
                throw EntityUtil.RelatedEndNotAttachedToContext(relatedEndName);
            }

            ObjectStateEntry entry = ObjectContext.ObjectStateManager.FindObjectStateEntry(this.Owner); 
            //Throw in case entity is in deleted state
            if (entry != null && entry.State == EntityState.Deleted) 
            { 
                throw EntityUtil.InvalidEntityStateLoad(relatedEndName);
            } 

            // MergeOption for Load must be NoTracking if and only if the source entity was NoTracking. If the source entity was
            // retrieved with any other MergeOption, the Load MergeOption can be anything but NoTracking. I.e. The entity could
            // have been loaded with OverwriteChanges and the Load option can be AppendOnly. 
            if (UsingNoTracking != (mergeOption == MergeOption.NoTracking))
            { 
                throw EntityUtil.MismatchedMergeOptionOnLoad(mergeOption); 
            }
 
            if (UsingNoTracking)
            {
                if (this.IsLoaded)
                { 
                    throw EntityUtil.LoadCalledOnAlreadyLoadedNoTrackedRelatedEnd();
                } 
 
                if (!IsEmpty())
                { 
                    throw EntityUtil.LoadCalledOnNonEmptyNoTrackedRelatedEnd();
                }
            }
 
            return sourceQuery;
        } 
 
        // -------
        // Methods 
        // -------

        /// 
        /// Loads the related entity or entities into the local related end using the default merge option. 
        /// 
        public void Load() 
        { 
            CheckOwnerNull();
            Load(DefaultMergeOption); 
        }

        /// 
        /// Loads the related entity or entities into the local related end using the supplied MergeOption. 
        /// 
        public abstract void Load(MergeOption mergeOption); 
 

        ///  
        /// Takes a list of related entities and merges them into the current collection.
        /// 
        /// Entities to relate to the owner of this EntityCollection
        /// MergeOption to use when updating existing relationships 
        /// Indicates whether IsLoaded should be set to true after the Load is complete.
        /// Should be false in cases where we cannot guarantee that the set of entities is complete 
        /// and matches the server, such as Attach. 
        protected void Merge(IEnumerable collection, MergeOption mergeOption, bool setIsLoaded)
        { 
            //Dev note: do not add event firing in Merge API, if it need to be added, add it to the caller

            List refreshedCollection = collection as List;
            if (null == refreshedCollection) 
            {
                refreshedCollection = new List(collection); 
            } 

            EntityKey sourceKey = ObjectStateManager.FindKeyOnEntityWithRelationships(Owner); 
            EntityUtil.CheckEntityKeyNull(sourceKey);

            ObjectStateManager.UpdateRelationships(this.ObjectContext, mergeOption, (AssociationSet)RelationshipSet, (AssociationEndMember)FromEndProperty, sourceKey, this.Owner, (AssociationEndMember)ToEndMember, refreshedCollection, setIsLoaded);
 
            if (setIsLoaded)
            { 
                // If the input collection contains all related entities, mark the collection as "loaded" 
                _isLoaded = true;
            } 
        }

        /// 
        /// Attaches an entity to the related end. If the related end is already filled 
        /// or partially filled, this merges the existing entities with the given entity. The given
        /// entity is not assumed to be the complete set of related entities. 
        /// 
        /// Owner and all entities passed in must be in Unchanged or Modified state.
        /// Deleted elements are allowed only when the state manager is already tracking the relationship 
        /// instance.
        /// 
        /// The entity to attach to the related end
        /// Thrown when  is null. 
        /// Thrown when the entity cannot be related via the current relationship end.
        void IRelatedEnd.Attach(IEntityWithRelationships entity) 
        { 
            CheckOwnerNull();
            EntityUtil.CheckArgumentNull(entity, "entity"); 
            Attach(new IEntityWithRelationships[] { entity }, false);
        }

        internal protected void Attach(IEnumerable entities, bool allowCollection) 
        {
            ValidateOwnerForAttach(); 
 
            // validate children and collect them in the "refreshedCollection" for this instance
            int index = 0; 
            List collection = new List();

            foreach (TEntity entity in entities)
            { 
                ValidateEntityForAttach(entity, index++, allowCollection);
                collection.Add(entity); 
            } 

            _suppressEvents = true; 
            try
            {
                // After Attach, the two entities should be related in the Unchanged state, so use OverwriteChanges
                // Since no query is done in this case, the MergeOption only controls the relationships 
                Merge(collection, MergeOption.OverwriteChanges, false /*setIsLoaded*/);
            } 
            finally 
            {
                _suppressEvents = false; 
            }
            OnAssociationChanged(CollectionChangeAction.Refresh, null);
        }
 
        // verifies requirements for Owner in Attach()
        internal protected void ValidateOwnerForAttach() 
        { 
            if (null == this.ObjectContext || UsingNoTracking)
            { 
                throw EntityUtil.InvalidOwnerStateForAttach();
            }

            // find state entry 
            ObjectStateEntry stateEntry = this.ObjectContext.ObjectStateManager.GetObjectStateEntry(this.Owner);
            if (stateEntry.State != EntityState.Modified && 
                stateEntry.State != EntityState.Unchanged) 
            {
                throw EntityUtil.InvalidOwnerStateForAttach(); 
            }
        }

        // verifies requirements for child entity passed to Attach() 
        internal protected void ValidateEntityForAttach(TEntity entity, int index, bool allowCollection)
        { 
            if (null == entity) 
            {
                if (allowCollection) 
                {
                    throw EntityUtil.InvalidNthElementNullForAttach(index);
                }
                else 
                {
                    throw EntityUtil.ArgumentNull("entity"); 
                } 
            }
 
            // verify the entity exists in the current context
            Debug.Assert(null != this.ObjectContext,
                "ObjectContext must not be null after call to ValidateOwnerForAttach");
            Debug.Assert(!UsingNoTracking, "We should not be here for NoTracking case."); 
            ObjectStateEntry stateEntry = this.ObjectContext.ObjectStateManager.FindObjectStateEntry(entity);
            if (null == stateEntry || !Object.ReferenceEquals(stateEntry.Entity, entity)) 
            { 
                if (allowCollection)
                { 
                    throw EntityUtil.InvalidNthElementContextForAttach(index);
                }
                else
                { 
                    throw EntityUtil.InvalidEntityContextForAttach();
                } 
            } 
            Debug.Assert(stateEntry.State != EntityState.Detached,
                "State cannot be detached if the entry was retrieved from the context"); 

            // verify the state of the entity (may not be in added state, since we only support attaching relationships
            // to existing entities)
            if (stateEntry.State != EntityState.Unchanged && 
                stateEntry.State != EntityState.Modified)
            { 
                if (allowCollection) 
                {
                    throw EntityUtil.InvalidNthElementStateForAttach(index); 
                }
                else
                {
                    throw EntityUtil.InvalidEntityStateForAttach(); 
                }
            } 
        } 

        internal abstract IEnumerable CreateSourceQueryInternal(); 

        /// 
        ///   Adds an entity to the related end.  If the owner is
        ///   attached to a cache then the all the connected ends are 
        ///   added to the object cache and their corresponding relationships
        ///   are also added to the ObjectStateManager. The RelatedEnd of the 
        ///   relationship is also fixed. 
        /// 
        ///  
        ///   Entity instance of type IEntityWithRelationships to add to the related end
        /// 
        void IRelatedEnd.Add(IEntityWithRelationships entity)
        { 
            EntityUtil.CheckArgumentNull(entity, "entity");
            if (_owner != null) 
            { 
                Add(entity, /*applyConstraints*/true);
            } 
            else
            {
                // The related end is in a disconnected state, so the related end is just a container
                // A common scenario for this is during WCF deserialization 
                DisconnectedAdd(entity);
            } 
        } 

        ///  
        ///   Removes an entity from the related end.  If owner is
        ///   attached to a cache, marks relationship for deletion and if
        ///   the relationship is composition also marks the entity for deletion.
        ///  
        /// 
        ///   Entity instance of type IEntityWithRelationships to remove from the related end 
        ///  
        /// Returns true if the entity was successfully removed, false if the entity was not part of the RelatedEnd.
        bool IRelatedEnd.Remove(IEntityWithRelationships entity) 
        {
            EntityUtil.CheckArgumentNull(entity, "entity");

            if (_owner != null) 
            {
                if (this.ContainsEntity(entity)) 
                { 

                    Remove(entity, /*fixup*/true, /*deleteEntity*/false, /*deleteOwner*/false, /*applyReferentialConstraints*/true); 
                    return true;
                }
                // The entity is not related so return false
                return false; 

            } 
            else 
            {
                // The related end is in a disconncted state, so the related end is just a container 
                // A common scenario for this is during WCF deserialization
                return DisconnectedRemove(entity);
            }
        } 

        internal abstract void DisconnectedAdd(IEntityWithRelationships entity); 
        internal abstract bool DisconnectedRemove(IEntityWithRelationships entity); 

        internal void Add(IEntityWithRelationships entity, bool applyConstraints) 
        {
            // Verify that the entity and owner are in a valid state before we try to do anything with them
            EntityUtil.ValidateRelationshipManager(entity);
            EntityUtil.ValidateRelationshipManager(_owner); 

            // SQLBU: 508819 508813 508752 
            // Detect as soon as possible if we are trying to readd entities which are in Deleted state. 
            // When one of the entity is in Deleted state, attempt would be made to readd this entity
            // to the OSM which is not allowed. 
            // NOTE: Current cleaning code (which uses cleanupOwnerEntity and cleanupPassedInEntity)
            // works only if one of the entity is not attached to the context.
            // PERFORMANCE: following can be performed faster if ObjectStateManager provide method to
            // lookup only in dictionary with Deleted entities (because here we are interestede only in Deleted entities) 
            if (_context != null && !UsingNoTracking)
            { 
                ValidateStateForAdd(_owner); 
                ValidateStateForAdd(entity);
            } 

            this.Add(entity,
                applyConstraints,
                false /*addRelationshipAsUnchanged*/, 
                false /*relationshipAlreadyExists*/);
 
        } 

        internal void CheckRelationEntitySet(EntitySet set) 
        {
            Debug.Assert(set != null, "null EntitySet");
            Debug.Assert(_relationshipSet != null,
                "Should only be checking the RelationshipSet on an attached entity and it should always be non-null in that case"); 

            if ((((AssociationSet)_relationshipSet).AssociationSetEnds[_navigation.To] != null) && 
                (((AssociationSet)_relationshipSet).AssociationSetEnds[_navigation.To].EntitySet != set)) 
            {
                throw EntityUtil.EntitySetIsNotValidForRelationship(set.EntityContainer.Name, set.Name, _navigation.To, _relationshipSet.EntityContainer.Name, _relationshipSet.Name); 
            }
        }

        internal void ValidateStateForAdd(IEntityWithRelationships entity) 
        {
            ObjectStateEntry entry = this.ObjectContext.ObjectStateManager.FindObjectStateEntry(entity); 
            if (entry != null && entry.State == EntityState.Deleted) 
            {
                throw EntityUtil.ObjectStateManagerDoesnotAllowToReAddUnchangedOrModifiedOrDeletedEntity(EntityState.Deleted); 
            }
        }

        internal void Add(IEntityWithRelationships targetEntity, bool applyConstraints, bool addRelationshipAsUnchanged, bool relationshipAlreadyExists) 
        {
            // Do verification 
            if (!this.VerifyEntityForAdd(targetEntity, relationshipAlreadyExists)) 
            {
                // Allow the same item to be "added" to a collection as a no-op operation 
                return;
            }

            EntityKey key = ObjectStateManager.FindKeyOnEntityWithRelationships(targetEntity); 
            if ((object)key != null && ObjectContext != null)
            { 
                CheckRelationEntitySet(key.GetEntitySet(ObjectContext.MetadataWorkspace)); 
            }
 
            RelatedEnd targetRelatedEnd = GetOtherEndOfRelationship(targetEntity);

            if (Object.ReferenceEquals(this.ObjectContext, targetRelatedEnd.ObjectContext) && this.ObjectContext != null)
            { 
                // Both entities are associated with the same non-null context
 
                // Make sure that they are either both tracked or both not tracked, or both don't have contexts 
                if (UsingNoTracking != targetRelatedEnd.UsingNoTracking)
                { 
                    throw EntityUtil.CannotCreateRelationshipBetweenTrackedAndNoTrackedEntities(UsingNoTracking ?
                        this._navigation.From : this._navigation.To);
                }
            } 
            else if(this.ObjectContext != null && targetRelatedEnd.ObjectContext != null)
            { 
                // Both entities have a context 
                if (UsingNoTracking && targetRelatedEnd.UsingNoTracking)
                { 
                    // Both entities are NoTracking, but have different contexts
                    // Attach the owner's context to the target's RelationshipManager
                    // O-C mappings are 1:1, so this operation is allowed
                    targetEntity.RelationshipManager.ResetContext(this.ObjectContext, GetTargetEntitySetFromRelationshipSet(), MergeOption.NoTracking); 
                }
                else 
                { 
                    // Both entities are already tracked by different non-null contexts
                    throw EntityUtil.CannotCreateRelationshipEntitiesInDifferentContexts(); 
                }
            }

            targetRelatedEnd.VerifyEntityForAdd(this.Owner, relationshipAlreadyExists); 

            // Do actual add 
 
            // Add the target entity to the source entity's collection or reference
            this.AddEntityToLocallyCachedCollection(targetEntity, applyConstraints); 

            // Fix up the target end of the relationship by adding the source entity to the target entity's collection or reference
            // devnote: applyConstraints should always be false in this fixup scenario,
            //          and should not just use the same value that was passed into this method. 
            targetRelatedEnd.AddEntityToLocallyCachedCollection(this.Owner, /*applyConstraints*/ false);
            // delay event firing for targetRelatedEnd. once we fire the event, we should be at operation completed state 
 
            // Ensure that both entities end up in the same context:
            // (1) If neither entity is attached to a context, we don't need to do anything else. 
            // (2) If they are both in the same one, we need to make sure neither one was created with MergeOption.NoTracking,
            //     and if not, add a relationship entry if it doesn't already exist.
            // (3) If both entities are already in different contexts, fail.
            // (4) Otherwise, only one entity is attached, and that is the context we will use. 
            //     For the entity that is not attached, attach it to that context.
 
            RelatedEnd attachedRelatedEnd = null; // the end of the relationship that is already attached to a context, if there is one. 
            IEntityWithRelationships entityToAdd = null; // the entity to be added to attachedRelatedEnd
            HashSet promotedEntityKeyRefs = new HashSet(); 

            if (Object.ReferenceEquals(this.ObjectContext, targetRelatedEnd.ObjectContext) && this.ObjectContext != null)
            {
                // Both entities are associated with the same non-null context 

                // Make sure that a relationship entry exists between these two entities. It is possible that the entities could 
                // have been added to the context independently of each other, so the relationship may not exist yet. 
                if (!relationshipAlreadyExists && !UsingNoTracking)
                { 
                    AddRelationshipToObjectStateManager(targetEntity, addRelationshipAsUnchanged, /*doAttach*/false);
                }
            }
            else if (this.ObjectContext != null || targetRelatedEnd.ObjectContext != null) 
            {
                // Only one entity has a context, so figure out which one it is, and determine which entity we will be adding to it 
                if (this.ObjectContext == null) 
                {
                    attachedRelatedEnd = targetRelatedEnd; 
                    entityToAdd = this.Owner;
                }
                else
                { 
                    attachedRelatedEnd = this;
                    entityToAdd = targetEntity; 
                } 

                try 
                {
                    if (!attachedRelatedEnd.UsingNoTracking)
                    {
                        attachedRelatedEnd.AddGraphToObjectStateManager(entityToAdd, relationshipAlreadyExists, 
                            addRelationshipAsUnchanged, /*doAttach*/ false, promotedEntityKeyRefs);
                    } 
 
                    // Reset so we know we successfully added the graph and don't have to clean anything up
                    attachedRelatedEnd = null; 
                    entityToAdd = null;
                }
                finally
                { 
                    // if attachedRelatedEnd is still set, that means we failed during the add to the state manager, and need to clean up
                    if (attachedRelatedEnd != null) 
                    { 
                        Debug.Assert(entityToAdd != null, "entityToAdd should be set if attachedRelatedEnd is set");
 
                        // Remove the source entity from the target related end
                        attachedRelatedEnd.FixupOtherEndOfRelationshipForRemove(entityToAdd);

                        // Remove the target entity from the source related end 
                        attachedRelatedEnd.RemoveEntityFromLocallyCachedCollection(entityToAdd, /*resetIsLoaded*/ false);
 
                        // Remove the graph that we just tried to add to the context 
                        RelationshipManager.RemoveRelatedEntitiesFromObjectStateManager(entityToAdd, promotedEntityKeyRefs);
                        Debug.Assert(promotedEntityKeyRefs.Count == 0, "Haven't cleaned up all of the promoted reference EntityKeys"); 
                        RemoveEntityFromObjectStateManager(entityToAdd);
                    }
                }
            } 

            // else neither entity is associated with a context, so there is no state manager to update 
            // fire the Association changed event, first on targetRelatedEnd then on this EC 
            targetRelatedEnd.OnAssociationChanged(CollectionChangeAction.Add, this.Owner);
            OnAssociationChanged(CollectionChangeAction.Add, targetEntity); 
        }

        private void AddGraphToObjectStateManager(IEntityWithRelationships entity, bool relationshipAlreadyExists,
            bool addRelationshipAsUnchanged, bool doAttach, HashSet promotedEntityKeyRefs) 
        {
            Debug.Assert(!UsingNoTracking, "Should not be attempting to add graphs to the state manager with NoTracking related ends"); 
 
            AddEntityToObjectStateManager(entity, doAttach);
            if (!relationshipAlreadyExists) 
            {
                AddRelationshipToObjectStateManager(entity, addRelationshipAsUnchanged, doAttach);
            }
            WalkObjectGraphToIncludeAllRelatedEntities(entity, addRelationshipAsUnchanged, doAttach, promotedEntityKeyRefs); 
        }
 
        internal void Remove(IEntityWithRelationships entity, bool doFixup, bool deleteEntity, bool deleteOwner, bool applyReferentialConstraints) 
        {
            // Verify that the entity and owner are in a valid state before we try to do anything with them 
            EntityUtil.ValidateRelationshipManager(entity);
            EntityUtil.ValidateRelationshipManager(_owner);

            if (!this.ContainsEntity(entity)) 
            {
                return; 
            } 

            // There can be a case when symmetrical Remove() shall be performed because of Referential Constraints 
            // Example:
            //   Relationshipo Client -> Order with Referential Constraint on in.
            //   When user calls (pseudo code) Order.Remove(Client), we perform Client.Remove(Order),
            //   because removing relationship between Client and Order should cause cascade delete on the Order side. 
            if (null != _context && doFixup &&
                applyReferentialConstraints && 
                IsDependentEndOfReferentialConstraint()) 
            {
                // Remove _owner from the related end with applying Referential Constraints 
                RelatedEnd relatedEnd = GetOtherEndOfRelationship(entity);
                relatedEnd.Remove(_owner, doFixup, deleteEntity, deleteOwner, applyReferentialConstraints);

                return; 
            }
 
 
            //The following call will verify that the given entity is part of the collection or ref.
            bool fireEvent = RemoveEntityFromLocallyCachedCollection(entity, false); 

            if (!UsingNoTracking)
            {
                MarkRelationshipAsDeletedInObjectStateManager(entity, _owner, _relationshipSet, _navigation); 
            }
 
            if (doFixup) 
            {
                bool deleteRelatedEntity = false; 

                //The related end "entity" cannot live without this side "owner". It should be deleted. Cascade this
                // effect to related enteties of the "related" entity
                if (null != _context && (deleteEntity || 
                    (deleteOwner && CheckCascadeDeleteFlag(_fromEndProperty)) ||
                    (applyReferentialConstraints && IsPrincipalEndOfReferentialConstraint()))) 
                { 
                    //Once related entity is deleted, all relationships involving related entity would be updated
                    deleteRelatedEntity = true; 
                }

                // RemoveEntityFromRelatedEnds check for graph circularities to make sure
                // it does not get into infinite loop 
                if (deleteRelatedEntity)
                { 
                    RemoveEntityFromRelatedEnds(entity, _owner, _navigation.Reverse); 
                }
 
                FixupOtherEndOfRelationshipForRemove(entity);

                if (deleteRelatedEntity)
                { 
                    MarkEntityAsDeletedInObjectStateManager(entity);
                } 
            } 

            if (fireEvent) 
            {
                OnAssociationChanged(CollectionChangeAction.Remove, entity);
            }
        } 

        ///  
        /// Check if current RelatedEnd is a Dependent end of some Referential Constraint 
        /// 
        internal bool IsDependentEndOfReferentialConstraint() 
        {
            if (null != _relationMetadata)
            {
                // NOTE Referential constraints collection will usually contains 0 or 1 element, 
                // so performance shouldn't be an issue here
                foreach (ReferentialConstraint constraint in ((AssociationType)_relationMetadata).ReferentialConstraints) 
                { 
                    if (constraint.ToRole == this._fromEndProperty)
                    { 
                        // Example:
                        //    Client --- Order
                        //    RI Constraint: Principal/From ,  Dependent/To 
                        // When current RelatedEnd is a CollectionOrReference in Order's relationships, 
                        // constarint.ToRole == this._fromEndProperty == Order
                        return true; 
                    } 
                }
            } 
            return false;
        }

        ///  
        /// Check if current RelatedEnd is a Principal end of some Referential Constraint
        ///  
        internal bool IsPrincipalEndOfReferentialConstraint() 
        {
            if (null != _relationMetadata) 
            {
                // NOTE Referential constraints collection will usually contains 0 or 1 element,
                // so performance shouldn't be an issue here
                foreach (ReferentialConstraint constraint in ((AssociationType)_relationMetadata).ReferentialConstraints) 
                {
                    if (constraint.FromRole == this._fromEndProperty) 
                    { 
                        // Example:
                        //    Client --- Order 
                        //    RI Constraint: Principal/From ,  Dependent/To 
                        // When current RelatedEnd is a CollectionOrReference in Client's relationships,
                        // constarint.FromRole == this._fromEndProperty == Client
                        return true; 
                    }
                } 
            } 
            return false;
        } 

        //Add given entity and its relationship to ObjectStateManager. Walk graph to recursively
        // add all entities in the graph.
        // If doAttach==TRUE, the entities are attached directly as Unchanged without calling AcceptChanges() 
        internal void IncludeEntity(U entity, bool addRelationshipAsUnchanged, bool doAttach, HashSet promotedEntityKeyRefs)
            where U : class, IEntityWithRelationships 
        { 
            Debug.Assert(!UsingNoTracking, "Should not be trying to include entities in the state manager for NoTracking related ends");
 
            //check to see if entity is already added to the cache
            //search by object reference so that we will not find any entries with the same key but a different object instance
            // NOTE: if (cacheEntry.Entity == entity) then this part of the graph is skipped
            ObjectStateEntry cacheEntry = _context.ObjectStateManager.FindObjectStateEntry(entity); 
            Debug.Assert(cacheEntry == null || cacheEntry.Entity == entity,
                    "Expected to have looked up this state entry by reference, how did we get a different entity?"); 
 
            if (null == cacheEntry ||
                cacheEntry.State == EntityState.Deleted) 
            {
                // NOTE (Attach): if (null == entity.Key) then check must be performed whether entity really
                // doesn't exist in the context (by creating fake Key and calling FindObjectStateEntry(Key) )
                // This is done in the ObjectContext::AttachSingleObject(). 

                AddGraphToObjectStateManager(entity, /*relationshipAlreadyExists*/ false, 
                    addRelationshipAsUnchanged, doAttach, promotedEntityKeyRefs); 
            }
 
            // There is a possibility that related entity is added to cache but relationship is not added.
            // Example: Suppose A and B are related. When walking the graph it is possible that
            // node B was visited through some relationship other than A-B.
            else if (null == FindRelationshipEntryInObjectStateManager(entity)) 
            {
                // If we have a reference with a detached key, make sure the key matches the relationship we are about to add 
                EntityReference entityRef = this as EntityReference; 
                if (entityRef != null && entityRef.DetachedEntityKey != null)
                { 
                    EntityKey targetKey = _context.ObjectStateManager.GetEntityKey(entity);
                    if (entityRef.DetachedEntityKey != targetKey)
                    {
                        throw EntityUtil.EntityKeyValueMismatch(); 
                    }
                    // else -- null just means the key isn't set, so the target entity key doesn't also have to be null 
                } 

                AddRelationshipToObjectStateManager(entity, addRelationshipAsUnchanged, doAttach); 
            }
            // else relationship is already there, nothing more to do
        }
 
        // Remove given entity and its relationship from ObjectStateManager.
        // Traversegraph to recursively remove all entities in the graph. 
        internal void ExcludeEntity(U entity, HashSet promotedEntityKeyRefs) 
            where U : class, IEntityWithRelationships
        { 
            Debug.Assert(!UsingNoTracking, "Should not try to exclude entities from the state manager for NoTracking related ends.");
            //check to see if entity is already removed from the cache
            ObjectStateEntry cacheEntry = _context.ObjectStateManager.FindObjectStateEntry(entity);
 
            if (null != cacheEntry && cacheEntry.State != EntityState.Deleted && !entity.RelationshipManager.NodeVisited)
            { 
                entity.RelationshipManager.NodeVisited = true; 

                RelationshipManager.RemoveRelatedEntitiesFromObjectStateManager(entity, promotedEntityKeyRefs); 
                RemoveRelationshipFromObjectStateManager(entity, _owner, _relationshipSet, _navigation);
                RemoveEntityFromObjectStateManager(entity);
            }
            // There is a possibility that related entity is removed from cache but relationship is not removed. 
            // Example: Suppose A and B are related. When walking the graph it is possible that
            // node B was visited through some relationship other than A-B. 
            else if (null != FindRelationshipEntryInObjectStateManager(entity)) 
            {
                RemoveRelationshipFromObjectStateManager(entity, _owner, _relationshipSet, _navigation); 
            }
        }

        internal ObjectStateEntry FindRelationshipEntryInObjectStateManager(IEntityWithRelationships entity) 
        {
            Debug.Assert(!UsingNoTracking, "Should not look for RelationshipEntry in ObjectStateManager for NoTracking cases."); 
            EntityKey entityKey = ObjectContext.FindEntityKey(entity, this._context); 
            EntityKey ownerKey = ObjectContext.FindEntityKey(_owner, this._context);
            return this._context.ObjectStateManager.FindRelationship(_relationshipSet, 
                new KeyValuePair(_navigation.From, ownerKey),
                new KeyValuePair(_navigation.To, entityKey));
        }
 
        internal void Clear(IEntityWithRelationships entity, RelationshipNavigation navigation, bool doCascadeDelete)
        { 
            ClearCollectionOrRef(entity, navigation, doCascadeDelete); 
        }
 
        // Check if related entities contain proper property values
        // (entities with temporary keys are skiped)
        internal bool CheckReferentialConstraintProperties(EntityKey ownerKey)
        { 
            // if the related end contains a real entity or is a reference with a detached entitykey, we need to check for RI constraints
            if (!this.IsEmpty() || 
                ((ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne || 
                ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One) &&
                ((EntityReference)this).DetachedEntityKey != null)) 
            {
                foreach (ReferentialConstraint constraint in ((AssociationType)this.RelationMetadata).ReferentialConstraints)
                {
                    // Check properties in principals 
                    if (constraint.ToRole == FromEndProperty)
                    { 
                        if (!this.IsEmpty()) 
                        {
                            foreach (IEntityWithRelationships relatedEntity in this) 
                            {
                                EntitySet principalEntitySet = GetTargetEntitySetFromRelationshipSet();
                                // get or create a key to use to compare the values -- the target entity might not have been attached
                                // yet so it may not have a key, but we can create one here to use for checking the values 
                                EntityKey principalKey = ObjectContext.FindEntityKey(relatedEntity, this.ObjectContext);
                                if (null != (object)principalKey) 
                                { 
                                    // Validate the key here because we need to get values from it for verification
                                    // and that will fail if the key is malformed. 
                                    // Verify only if the key already exists.
                                    EntityUtil.ValidateEntitySetInKey(principalKey, principalEntitySet);
                                    principalKey.ValidateEntityKey(principalEntitySet);
                                } 
                                else
                                { 
                                    principalKey = _context.CreateEntityKey(principalEntitySet, relatedEntity); 
                                }
 
                                if (!VerifyRIConstraintsWithRelatedEntry(constraint, ownerKey, principalKey))
                                {
                                    return false;
                                } 
                                // else keep processing the other constraints
                            } 
                        } 
                        else
                        { 
                            // related end is empty, so we must have a reference with a detached key
                            EntityKey detachedKey = ((EntityReference)this).DetachedEntityKey;

                            // don't need to validate the principal/detached key here because that has already been done during AttachContext 
                            if (!VerifyRIConstraintsWithRelatedEntry(constraint, ownerKey, detachedKey))
                            { 
                                return false; 
                            }
                            // else keep processing the other constraints 
                        }
                    }
                    else
                    // Check properties in dependent (only if the related end is an empty EntityReference with EntityKey) 
                    if (constraint.FromRole == FromEndProperty && this.IsEmpty())
                    { 
                        // related end is empty, so we must have a reference with a detached key 
                        EntityKey detachedKey = ((EntityReference)this).DetachedEntityKey;
 
                        // don't need to validate the principal/detached key here because that has already been done during AttachContext
                        if (!VerifyRIConstraintsWithRelatedEntry(constraint, detachedKey, ownerKey))
                        {
                            return false; 
                        }
                        // else keep processing the other constraints 
                    } 
                }
            } 
            return true;
        }

        private static bool VerifyRIConstraintsWithRelatedEntry(ReferentialConstraint constraint, EntityKey dependentKey, EntityKey principalKey) 
        {
            Debug.Assert(constraint.FromProperties.Count == constraint.ToProperties.Count, "RIC: Referential constraints From/To properties list have different size"); 
 
            // NOTE order of properties in collections (From/ToProperties) is important.
            for (int i = 0; i < constraint.FromProperties.Count; ++i) 
            {
                string fromPropertyName = constraint.FromProperties[i].Name;
                string toPropertyName = constraint.ToProperties[i].Name;
 
                object currentValue = principalKey.FindValueByName(fromPropertyName);
                object expectedValue = dependentKey.FindValueByName(toPropertyName); 
 
                Debug.Assert(currentValue != null, "currentValue is part of Key on an attached entity, it must not be null");
 
                if (!currentValue.Equals(expectedValue))
                {
                    // RI Constraint violated
                    return false; 
                }
            } 
 
            return true;
        } 

        public IEnumerator GetEnumerator()
        {
            // Due to the way the CLR handles IEnumerator return types, the check for a null owner for EntityReferences 
            // must be made here because GetInternalEnumerator is delay-executed and so will not throw until the
            // enumerator is advanced 
            if (this is EntityReference) 
            {
                CheckOwnerNull(); 
            }
            return GetInternalEnumerator();
        }
 
        internal void RemoveAll()
        { 
            //copy into list because changing collection member is not allowed during enumeration. 
            // If possible avoid copying into list.
            List deletedEntities = null; 

            bool fireEvent = false;
            try
            { 
                _suppressEvents = true;
                foreach (IEntityWithRelationships e in this) 
                { 
                    if (null == deletedEntities)
                    { 
                        deletedEntities = new List();
                    }
                    deletedEntities.Add(e);
                } 

 
                if (fireEvent = (null != deletedEntities) && (deletedEntities.Count > 0)) 
                {
                    foreach (IEntityWithRelationships e in deletedEntities) 
                    {
                        Remove(e, /*fixup*/true, /*deleteEntity*/false, /*deleteOwner*/true, /*applyReferentialConstraints*/true);
                    }
                } 
            }
            finally 
            { 
                _suppressEvents = false;
            } 
            if (fireEvent)
            {
                OnAssociationChanged(CollectionChangeAction.Refresh, null);
            } 
        }
 
        internal void DetachAll(EntityState ownerEntityState) 
        {
            //copy into list because changing collection member is not allowed during enumeration. 
            // If possible avoid copying into list.
            List deletedEntities = new List();

            foreach (IEntityWithRelationships e in this) 
            {
                deletedEntities.Add(e); 
            } 

            bool detachRelationship = 
                    ownerEntityState == EntityState.Added ||
                    _fromEndProperty.RelationshipMultiplicity == RelationshipMultiplicity.Many;

            // every-fix up will fire with Remove action 
            // every forward operation (removing from this relatedEnd) will fire with Refresh
            // do not merge the loops, handle the related ends seperately (when the event is being fired, 
            // we should be in good state: for every entity deleted, related event should have been fired) 
            foreach (IEntityWithRelationships entity in deletedEntities)
            { 
                // future enhancement: it does not make sense to return in the half way, either remove this code or
                // move it to the right place
                if (!this.ContainsEntity(entity))
                { 
                    return;
                } 
 
                // if this is a reference, set the EntityKey property before removing the relationship and entity
                EntityReference entityReference = this as EntityReference; 
                if (entityReference != null)
                {
                    entityReference.DetachedEntityKey = entityReference.AttachedEntityKey;
                } 

                if (detachRelationship) 
                { 
                    DetachRelationshipFromObjectStateManager(entity, _owner, _relationshipSet, _navigation);
                } 
                RelatedEnd relatedEnd = GetOtherEndOfRelationship(entity);
                relatedEnd.RemoveEntityFromLocallyCachedCollection(_owner, /* resetIsLoaded */ true);
                relatedEnd.OnAssociationChanged(CollectionChangeAction.Remove, _owner);
            } 
            foreach (IEntityWithRelationships entity in deletedEntities)
            { 
                RelatedEnd relatedEnd = GetOtherEndOfRelationship(entity); 
                this.RemoveEntityFromLocallyCachedCollection(entity, /* resetIsLoaded */ false);
            } 
            this.OnAssociationChanged(CollectionChangeAction.Refresh, null);

            Debug.Assert(this.IsEmpty(), "Collection or reference should be empty");
        } 

        internal abstract bool VerifyEntityForAdd(IEntityWithRelationships entity, bool relationshipAlreadyExists); 
        internal abstract void AddEntityToLocallyCachedCollection(IEntityWithRelationships entity, bool applyConstraints); 
        internal abstract bool RemoveEntityFromLocallyCachedCollection(IEntityWithRelationships entity, bool resetIsLoaded);
        internal abstract void Include(bool addRelationshipAsUnchanged, bool doAttach, HashSet promotedEntityKeyRefs); 
        internal abstract void Exclude(HashSet promotedEntityKeyRefs);
        internal abstract void ClearCollectionOrRef(IEntityWithRelationships entity, RelationshipNavigation navigation, bool doCascadeDelete);
        internal abstract bool ContainsEntity(IEntityWithRelationships entity);
        internal abstract IEnumerator GetInternalEnumerator(); 
        internal abstract void RetrieveReferentialConstraintProperties(Dictionary> keyValues, HashSet visited);
        internal abstract bool IsEmpty(); 
        internal abstract void OnRelatedEndClear(); 

        internal virtual void OnAssociationChanged(CollectionChangeAction collectionChangeAction, object entity) 
        {
            if (!_suppressEvents)
            {
                if (_onAssociationChanged != null) 
                {
                    _onAssociationChanged(this, (new CollectionChangeEventArgs(collectionChangeAction, entity))); 
                } 
            }
        } 

        private void AddEntityToObjectStateManager(IEntityWithRelationships entity, bool doAttach)
        {
            Debug.Assert(_context != null, "Can't add to state manager if _context is null"); 
            Debug.Assert(!UsingNoTracking, "Should not add an Entity to ObjectStateManager for NoTracking cases.");
 
#if DEBUG 
            // we should have already verified that this entity does not exist at all, or that it's Deleted
            ObjectStateEntry cacheEntry = _context.ObjectStateManager.FindObjectStateEntry(entity); 
            Debug.Assert(cacheEntry == null || cacheEntry.State == EntityState.Deleted,
                "Expected to have looked up this state entry by reference, how did we get a different entity?");
#endif
 
            EntitySet es = GetTargetEntitySetFromRelationshipSet();
            if (!doAttach) 
            { 
                _context.AddSingleObject(es, entity, "entity");
            } 
            else
            {
                _context.AttachSingleObject(entity, es, "entity");
            } 

            // Now that we know we have a valid EntityKey for the target entity, verify that it matches the detached EntityKey, if there is one 
            EntityReference entityRef = this as EntityReference; 
            if (entityRef != null && entityRef.DetachedEntityKey != null)
            { 
                EntityKey targetKey = _context.ObjectStateManager.GetEntityKey(entity);
                if (entityRef.DetachedEntityKey != targetKey)
                {
                    throw EntityUtil.EntityKeyValueMismatch(); 
                }
                // else -- null just means the key isn't set, so the target entity key doesn't also have to be null 
            } 
        }
 
        internal EntitySet GetTargetEntitySetFromRelationshipSet()
        {
            EntitySet entitySet = null;
            AssociationSet associationSet = (AssociationSet)_relationshipSet; 
            Debug.Assert(associationSet != null, "(AssociationSet) cast failed");
 
            AssociationEndMember associationEndMember = (AssociationEndMember)ToEndMember; 
            Debug.Assert(associationEndMember != null, "(AssociationEndMember) cast failed");
 
            entitySet = associationSet.AssociationSetEnds[associationEndMember.Name].EntitySet;
            Debug.Assert(entitySet != null, "cannot find entitySet");
            return entitySet;
        } 

        private void AddRelationshipToObjectStateManager(IEntityWithRelationships entity, bool addRelationshipAsUnchanged, bool doAttach) 
        { 
            Debug.Assert(!UsingNoTracking, "Should not add Relationship to ObjectStateManager for NoTracking cases.");
            if (this._context != null && entity.RelationshipManager.Context != null) 
            {
                EntityKey ownerKey = this._context.ObjectStateManager.GetEntityKey(_owner);
                EntityKey entityKey = this._context.ObjectStateManager.GetEntityKey(entity);
 
                _context.ObjectStateManager.AddRelation(
                    new RelationshipWrapper((AssociationSet)_relationshipSet, 
                           new KeyValuePair(_navigation.From, ownerKey), 
                           new KeyValuePair(_navigation.To, entityKey)),
                    // When Add method is called through Load API the relationship cache entries 
                    // needs to be added to ObjectStateManager in Unchanged state rather then Added state
                    (addRelationshipAsUnchanged || doAttach) ? EntityState.Unchanged : EntityState.Added);
            }
        } 

        private static void WalkObjectGraphToIncludeAllRelatedEntities(IEntityWithRelationships entity, 
            bool addRelationshipAsUnchanged, bool doAttach, HashSet promotedEntityKeyRefs) 
        {
            foreach (RelatedEnd relatedEnd in entity.RelationshipManager.Relationships) 
            {
                relatedEnd.Include(addRelationshipAsUnchanged, doAttach, promotedEntityKeyRefs);
            }
        } 

        internal static void RemoveEntityFromObjectStateManager(IEntityWithRelationships entity) 
        { 
            ObjectStateEntry entry;
 
            if (entity.RelationshipManager.Context != null &&
                entity.RelationshipManager.Context.ObjectStateManager.IsAttachTracking &&
                entity.RelationshipManager.Context.ObjectStateManager.PromotedKeyEntries.TryGetValue(entity, out entry))
            { 
                // This is executed only in the cleanup code from ObjectContext.AttachTo()
                // If the entry was promoted in AttachTo(), it has to be degraded now instead of being deleted. 
                entry.DegradeEntry(); 
            }
            else 
            {
                entry = MarkEntityAsDeletedInObjectStateManager(entity);
                if (entry != null && entry.State != EntityState.Detached)
                { 
                    entry.AcceptChanges();
                } 
            } 
        }
 
        private static void RemoveRelationshipFromObjectStateManager(IEntityWithRelationships entity, IEntityWithRelationships owner, RelationshipSet relationshipSet, RelationshipNavigation navigation)
        {
            ObjectStateEntry deletedEntry = MarkRelationshipAsDeletedInObjectStateManager(entity, owner, relationshipSet, navigation);
            if (deletedEntry != null && deletedEntry.State != EntityState.Detached) 
            {
                deletedEntry.AcceptChanges(); 
            } 
        }
 
        private void FixupOtherEndOfRelationshipForRemove(IEntityWithRelationships entity)
        {
            RelatedEnd relatedEnd = GetOtherEndOfRelationship(entity);
            relatedEnd.Remove(_owner, /*fixup*/false, /*deleteEntity*/false, /*deleteOwner*/false, /*applyReferentialConstraints*/false); 
        }
 
        private static ObjectStateEntry MarkEntityAsDeletedInObjectStateManager(IEntityWithRelationships entity) 
        {
            ObjectStateEntry entry = null; 
            if (entity.RelationshipManager.Context != null)
            {
                entry = entity.RelationshipManager.Context.ObjectStateManager.FindObjectStateEntry(entity);
 
                if (entry != null)
                { 
                    entry.Delete(/*doFixup*/false); 
                }
            } 
            return entry;
        }

        private static ObjectStateEntry MarkRelationshipAsDeletedInObjectStateManager(IEntityWithRelationships entity, IEntityWithRelationships owner, RelationshipSet relationshipSet, RelationshipNavigation navigation) 
        {
            ObjectStateEntry entry = null; 
            if (owner.RelationshipManager.Context != null && entity.RelationshipManager.Context != null && relationshipSet != null) 
            {
                EntityKey ownerKey = owner.RelationshipManager.Context.ObjectStateManager.GetEntityKey(owner); 
                EntityKey entityKey = entity.RelationshipManager.Context.ObjectStateManager.GetEntityKey(entity);

                entry = entity.RelationshipManager.Context.ObjectStateManager.DeleteRelationship(relationshipSet,
                                      new KeyValuePair(navigation.From, ownerKey), 
                                      new KeyValuePair(navigation.To, entityKey));
            } 
            return entry; 
        }
 
        private static void DetachRelationshipFromObjectStateManager(IEntityWithRelationships entity, IEntityWithRelationships owner, RelationshipSet relationshipSet, RelationshipNavigation navigation)
        {
            if (owner.RelationshipManager.Context != null && entity.RelationshipManager.Context != null && relationshipSet != null)
            { 
                EntityKey ownerKey = owner.RelationshipManager.Context.ObjectStateManager.GetEntityKey(owner);
                EntityKey entityKey = entity.RelationshipManager.Context.ObjectStateManager.GetEntityKey(entity); 
                ObjectStateEntry entry = entity.RelationshipManager.Context.ObjectStateManager.FindRelationship(relationshipSet, 
                                                                          new KeyValuePair(navigation.From, ownerKey),
                                                                          new KeyValuePair(navigation.To, entityKey)); 
                if (entry != null)
                {
                    entry.DetachRelationshipEntry();
                } 
            }
        } 
 
        private static void RemoveEntityFromRelatedEnds(IEntityWithRelationships entity1, IEntityWithRelationships entity2, RelationshipNavigation navigation)
        { 
            foreach (RelatedEnd relatedEnd in entity1.RelationshipManager.Relationships)
            {
                bool doCascadeDelete = false;
                //check for cascade delete flag 
                doCascadeDelete = CheckCascadeDeleteFlag(relatedEnd.FromEndProperty) || relatedEnd.IsPrincipalEndOfReferentialConstraint();
                //Remove the owner from the related end 
                relatedEnd.Clear(entity2, navigation, doCascadeDelete); 
            }
        } 

        private static bool CheckCascadeDeleteFlag(RelationshipEndMember relationEndProperty)
        {
            if (null != relationEndProperty) 
            {
                return (relationEndProperty.DeleteBehavior == OperationAction.Cascade); 
            } 
            return false;
        } 


        internal void AttachContext(ObjectContext context, MergeOption mergeOption)
        { 
            EntityUtil.CheckKeyForRelationship(_owner, mergeOption);
            EntityKey ownerKey = ObjectContext.FindEntityKey(_owner, context); 
            EntityUtil.CheckEntityKeyNull(ownerKey); 
            EntitySet entitySet = ownerKey.GetEntitySet(context.MetadataWorkspace);
 
            AttachContext(context, entitySet, mergeOption);
        }

 
        /// 
        /// Set the context and load options so that Query can be 
        /// constructed on demand. 
        /// 
        internal void AttachContext(ObjectContext context, EntitySet entitySet, MergeOption mergeOption) 
        {
            EntityUtil.CheckArgumentNull(context, "context");
            EntityUtil.CheckArgumentMergeOption(mergeOption);
            EntityUtil.CheckArgumentNull(entitySet, "entitySet"); 

            ((IEntityWithRelationships)_owner).RelationshipManager.NodeVisited = false; 
            // If the context is the same as what we already have, and the mergeOption is consistent with our UsingNoTracking setting, nothing more to do 
            if (_context == context && (_usingNoTracking == (mergeOption == MergeOption.NoTracking)))
            { 
                return;
            }

            bool doCleanup = true; 

            try 
            { 
                // if the source isn't null, clear it
                _sourceQuery = null; 
                this._context = context;
                this._usingNoTracking = (mergeOption == MergeOption.NoTracking);

                // find the relationship set 
                Debug.Assert(_context.MetadataWorkspace != null, "The context should not have a null metadata workspace.");
 
                // find the TypeMetadata for the given relationship 
                EdmType relationshipType = _context.MetadataWorkspace.GetItem(_navigation.RelationshipName, DataSpace.CSpace);
                if (relationshipType == null) 
                {
                    throw EntityUtil.NoRelationshipSetMatched(_navigation.RelationshipName);
                }
 
                // find the RelationshipSet
                RelationshipSet relationshipSet = null; 
                foreach (EntitySetBase entitySetBase in entitySet.EntityContainer.BaseEntitySets) 
                {
                    if ((EdmType)entitySetBase.ElementType == relationshipType) 
                    {
                        if (((AssociationSet)entitySetBase).AssociationSetEnds[_navigation.From].EntitySet == entitySet)
                        {
                            relationshipSet = (RelationshipSet)entitySetBase; 
                            break;
                        } 
                    } 
                }
 
                if (relationshipSet != null)
                {
                    this._relationshipSet = relationshipSet;
                    this._relationMetadata = (RelationshipType)relationshipType; 
                }
                else 
                { 
                    foreach (EntitySetBase set in entitySet.EntityContainer.BaseEntitySets)
                    { 
                        AssociationSet associationset = set as AssociationSet;
                        if (associationset != null)
                        {
                            if (associationset.ElementType == relationshipType && 
                                associationset.AssociationSetEnds[_navigation.From].EntitySet != entitySet &&
                                associationset.AssociationSetEnds[_navigation.From].EntitySet.ElementType == entitySet.ElementType) 
                                throw EntityUtil.EntitySetIsNotValidForRelationship(entitySet.EntityContainer.Name, entitySet.Name, _navigation.From ,((AssociationSet)set).EntityContainer.Name,((AssociationSet)set).Name); 
                        }
                    } 
                    throw EntityUtil.NoRelationshipSetMatched(_navigation.RelationshipName);
                }

                //find relation end property 
                bool foundFromRelationEnd = false;
                bool foundToRelationEnd = false; 
                foreach (AssociationEndMember relationEnd in ((AssociationType)_relationMetadata).AssociationEndMembers) //Only Association relationship is supported 
                {
                    if (relationEnd.Name == this._navigation.From) 
                    {
                        if (foundFromRelationEnd)
                        {
                            throw EntityUtil.FoundMoreThanOneRelatedEnd(); 
                        }
                        foundFromRelationEnd = true; 
                        this._fromEndProperty = relationEnd; 
                    }
                    if (relationEnd.Name == this._navigation.To) 
                    {
                        if (foundToRelationEnd)
                        {
                            throw EntityUtil.FoundMoreThanOneRelatedEnd(); 
                        }
                        foundToRelationEnd = true; 
                        this._toEndProperty = relationEnd; 
                    }
                } 
                if (!(foundFromRelationEnd && foundToRelationEnd))
                {
                    throw EntityUtil.RelatedEndNotFound();
                } 

                // If this is a stub EntityReference and the DetachedEntityKey is set, make sure it is valid 
                if (this.IsEmpty()) 
                {
                    // if there are no contained entities but this is a reference with a detached entity key, validate the key 
                    EntityReference entityRef = this as EntityReference;
                    if (entityRef != null && entityRef.DetachedEntityKey != null)
                    {
                        EntityKey detachedKey = entityRef.DetachedEntityKey; 
                        if (!IsValidEntityKeyType(detachedKey))
                        { 
                            // devnote: We have to check this here instead of in the EntityKey property setter, 
                            //          because the key could be set to an invalid type temporarily during deserialization
                            throw EntityUtil.CannotSetSpecialKeys(); 
                        }
                        EntitySet targetEntitySet = detachedKey.GetEntitySet(context.MetadataWorkspace);
                        CheckRelationEntitySet(targetEntitySet);
                        detachedKey.ValidateEntityKey(targetEntitySet); 
                    }
                } 
                // else even for a reference we don't need to validate the key 
                // because it will be checked later once we have the key for the contained entity
 
                doCleanup = false;
            }
            finally
            { 
                if (doCleanup)
                { 
                    // Uninitialize fields, so the cleanup code (for example in RelationshipWrapper.RemoveRelatedEntitiesFromObjectStateManager) 
                    // knows that this RelatedEnd was not properly Attached.
                    this.DetachContext(); 
                }
            }
        }
 
        /// 
        /// Clear the source and context. 
        ///  
        internal void DetachContext()
        { 
            this._sourceQuery = null;
            this._context = null;
            this._relationshipSet = null;
            this._fromEndProperty = null; 
            this._toEndProperty = null;
 
            // Detached entity should have IsLoaded property set to false 
            this._isLoaded = false;
        } 

        /// 
        ///  This method is very similar to the private method in Query of U class
        ///  Only difference is that it calls meterializer with a overload which does 
        ///  not skip the deleted items.
        ///  
        /// query of U 
        /// 
        internal static IEnumerable GetResults(ObjectQuery query) 
        {
            return query.Execute(query.MergeOption);
        }
 
        internal RelatedEnd GetOtherEndOfRelationship(IEntityWithRelationships entity)
        { 
            return (RelatedEnd)entity.RelationshipManager.GetRelatedEnd(_navigation.Reverse, _relationshipFixer); 
        }
 
        // We have to allow a default constructor for serialization, so we need to make sure that the only
        // thing you can do with a null owner is get/set the EntityReference.EntityKey property. All other
        // operations are invalid. This needs to be used on all public methods in this class and EntityReference
        // but not in EntityCollection because EntityCollection does not have a default constructor. 
        // It is not possible to get an EntityReference with a null Owner into the RelationshipManager, and there
        // is no way to access EntityReference without creating one using the default constructor or going through 
        // the RelationshipManager, so we don't need to check this in internal or private methods. 
        internal void CheckOwnerNull()
        { 
            if (_owner == null)
            {
                throw EntityUtil.OwnerIsNull();
            } 
        }
 
        // This method is intended to be used to support the public API InitializeRelatedReference, where we have to take an existing EntityReference 
        // and set up the appropriate fields as shown below, instead of creating a new EntityReference and setting these fields in the constructor.
        // This is also used by the constructor -- if we add something that needs to be set at construction time, it probably needs to be set for InitializeRelatedReference as well. 
        internal void InitializeRelatedEnd(IEntityWithRelationships owner, RelationshipNavigation navigation, IRelationshipFixer relationshipFixer)
        {
            _owner = owner;
            _navigation = navigation; 
            _relationshipFixer = relationshipFixer;
        } 
 
        internal static bool IsValidEntityKeyType(EntityKey entityKey)
        { 
            return !(entityKey.IsTemporary || Object.ReferenceEquals(EntityKey.EntityNotValidKey, entityKey) || Object.ReferenceEquals(EntityKey.NoEntitySetKey, entityKey));
        }
    }
} 

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