File: System\Data\Objects\DataClasses\RelatedEnd.cs
Project: ndp\fx\src\DataEntity\System.Data.Entity.csproj (System.Data.Entity)
//---------------------------------------------------------------------
// <copyright file="RelatedEnd.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner       Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
 
namespace System.Data.Objects.DataClasses
{
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data.Common.Utils;
    using System.Data.Metadata.Edm;
    using System.Data.Objects.Internal;
    using System.Diagnostics;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.Text;
 
    /// <summary>
    /// Base class for EntityCollection and EntityReference
    /// </summary>
 
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
    [DataContract]
    [Serializable]
    public abstract class RelatedEnd : IRelatedEnd
    {
 
        //-----------------
        // Internal Constructors
        //-----------------
 
        /// <summary>
        /// The default constructor is required for some serialization scenarios with EntityReference.
        /// </summary>
        internal RelatedEnd()
        {
            _wrappedOwner = EntityWrapperFactory.NullWrapper;
        }
 
        internal RelatedEnd(IEntityWrapper wrappedOwner, RelationshipNavigation navigation, IRelationshipFixer relationshipFixer)
        {
            EntityUtil.CheckArgumentNull(wrappedOwner, "wrappedOwner");
            EntityUtil.CheckArgumentNull(wrappedOwner.Entity, "wrappedOwner.Entity");
            EntityUtil.CheckArgumentNull(navigation, "navigation");
            EntityUtil.CheckArgumentNull(relationshipFixer, "fixer");
 
            InitializeRelatedEnd(wrappedOwner, 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
 
        /// <summary>
        /// Note that this field should no longer be used directly.  Instead, use the _wrappedOwner
        /// field.  This field is retained only for compatibility with the serialization format introduced in v1.
        /// </summary>
        [Obsolete()]
        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]
        private IEnumerable<EdmMember> _sourceQueryParamProperties; // indicates which properties populate query parameters
 
        [NonSerialized]
        internal bool _suppressEvents;
        [NonSerialized]
        internal CollectionChangeEventHandler _onAssociationChanged;
 
        [NonSerialized]
        private IEntityWrapper _wrappedOwner;
 
        // ------
        // Events
        // ------
 
        /// <summary>
        /// Event to notify changes in the Associations.
        /// </summary>
        public event CollectionChangeEventHandler AssociationChanged
        {
            add
            {
                CheckOwnerNull();
                _onAssociationChanged += value;
            }
            remove
            {
                CheckOwnerNull();
                _onAssociationChanged -= value;
            }
        }
 
        /// <summary>internal event to notify change in collection</summary>
        internal virtual event CollectionChangeEventHandler AssociationChangedForObjectView
        {
            // we fire this event only from EntityCollection, definitely not from EntityReference
            add { Debug.Assert(false, "should never happen"); }
            remove { Debug.Assert(false, "should never happen"); }
        }
 
 
        // ----------
        // Properties
        // ----------
 
 
        internal bool IsForeignKey
        {
            get
            {
                Debug.Assert(this.ObjectContext != null, "the IsForeignKey property shouldn't be used in detached scenarios");
                Debug.Assert(this._relationMetadata != null, "this._relationMetadata == null");
 
                return ((AssociationType)_relationMetadata).IsForeignKey;
            }
        }
 
        /// <summary>
        /// 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.
        /// </summary>
        internal RelationshipNavigation RelationshipNavigation
        {
            get
            {
                return _navigation;
            }
        }
 
        /// <summary>
        /// Name of the relationship in which this RelatedEnd is participating        
        /// </summary>
        [System.Xml.Serialization.SoapIgnore]
        [System.Xml.Serialization.XmlIgnore]
        public string RelationshipName
        {
            get
            {
                CheckOwnerNull();
                return _navigation.RelationshipName;
            }
        }
 
        /// <summary>
        /// Name of the relationship source role used to generate this RelatedEnd
        /// </summary>
        [System.Xml.Serialization.SoapIgnore]
        [System.Xml.Serialization.XmlIgnore]
        public string SourceRoleName
        {
            get
            {
                CheckOwnerNull();
                return _navigation.From;
            }
        }
 
        /// <summary>
        /// Name of the relationship target role used to generate this RelatedEnd
        /// </summary>
        [System.Xml.Serialization.SoapIgnore]
        [System.Xml.Serialization.XmlIgnore]
        public string TargetRoleName
        {
            get
            {
                CheckOwnerNull();
                return _navigation.To;
            }
        }
 
        IEnumerable IRelatedEnd.CreateSourceQuery()
        {
            CheckOwnerNull();
            return this.CreateSourceQueryInternal();
        }
 
        internal IEntityWrapper WrappedOwner
        {
            get
            {
                return this._wrappedOwner;
            }
        }
 
        internal ObjectContext ObjectContext
        {
            get
            {
                return this._context;
            }
        }
 
        internal virtual void BulkDeleteAll(System.Collections.Generic.List<object> list)
        {
            throw EntityUtil.NotSupported();
        }
 
        /// <summary>
        /// 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
        /// </summary>
        [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;
            }
        }
 
        /// <summary>
        /// IsLoaded returns true if and only if Load was called.
        /// </summary>
        [System.Xml.Serialization.SoapIgnore]
        [System.Xml.Serialization.XmlIgnore]
        public bool IsLoaded
        {
            get
            {
                CheckOwnerNull();
                return this._isLoaded;
            }
        }
 
        internal void SetIsLoaded(bool value)
        {
            this._isLoaded = value;
        }
 
        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="mergeOption">MergeOption to use when creating the query</param>
        /// <param name="hasResults">Indicates whether the query can produce results.
        /// For instance, a lookup with null key values cannot produce results.</param>
        /// <returns>The query loading related entities.</returns>
        internal ObjectQuery<TEntity> CreateSourceQuery<TEntity>(MergeOption mergeOption, out bool hasResults)
        {
            // must have a context
            if (_context == null)
            {
                hasResults = false;
                return null;
            }
 
            EntityEntry stateEntry = _context.ObjectStateManager.FindEntityEntry(_wrappedOwner.Entity);
            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, unless this is the dependent end of an FK relationship
            if (entityState == EntityState.Added &&
                (!IsForeignKey ||
                 !IsDependentEndOfReferentialConstraint(checkIdentifying: false)))
            {
                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
            // Added/Modified/Unchanged/Deleted and mergeOption is NOT NoTracking
            // (if entity is attached to the context, mergeOption should never be NoTracking)
            // If the entity state is added, at this point it is an FK dependent end
            if (!((entityState == EntityState.Detached && UsingNoTracking) ||
                   entityState == EntityState.Modified ||
                   entityState == EntityState.Unchanged ||
                   entityState == EntityState.Deleted ||
                   entityState == EntityState.Added))
            {
                hasResults = false;
                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 = _wrappedOwner.EntityKey;
            EntityUtil.CheckEntityKeyNull(key); // Might be null because of faulty IPOCO implementation
 
            // If the source query text has not been initialized, then do so now.
            if (null == _sourceQuery)
            {
                Debug.Assert(_relationshipSet.BuiltInTypeKind == BuiltInTypeKind.AssociationSet, "Non-AssociationSet Relationship Set?");
                AssociationType associationMetadata = (AssociationType)_relationMetadata;
 
                EntitySet ownerEntitySet = ((AssociationSet)_relationshipSet).AssociationSetEnds[_fromEndProperty.Name].EntitySet;
                EntitySet targetEntitySet = ((AssociationSet)_relationshipSet).AssociationSetEnds[_toEndProperty.Name].EntitySet;
 
                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;
                }
 
                StringBuilder sourceBuilder;
                if (associationMetadata.IsForeignKey)
                {
                    var fkConstraint = associationMetadata.ReferentialConstraints[0];
                    var principalProps = fkConstraint.FromProperties;
                    var dependentProps = fkConstraint.ToProperties;
                    Debug.Assert(principalProps.Count == dependentProps.Count, "Mismatched foreign key properties?");
 
                    if (fkConstraint.ToRole.EdmEquals(_toEndProperty))
                    {
                        // This related end goes from 'principal' to 'dependent', and has the key of the principal.
                        // In this case it is sufficient to filter the target (dependent) set where the foreign key
                        // properties have the same values as the corresponding entity key properties from the principal.
                        //
                        // SELECT VALUE D 
                        //   FROM OfType(##DependentEntityset, ##DependentEntityType) 
                        // AS D
                        // WHERE 
                        //   D.DependentProperty1 = @PrincipalProperty1 [AND
                        //   ...
                        //   D.DependentPropertyN = @PrincipalPropertyN]
                        //
                        // Note that the OfType operator can be omitted if the element type of ##DependentEntitySet
                        // is equal to the Entity type produced by the target end of the relationship.
                        sourceBuilder = new StringBuilder("SELECT VALUE D FROM ");
                        AppendEntitySet(sourceBuilder, targetEntitySet, targetEntityType, ofTypeRequired);
                        sourceBuilder.Append(" AS D WHERE ");
 
                        // For each principal key property there is a corresponding query parameter that supplies the value
                        // from this owner's entity key, so KeyParam1 corresponds to the first key member, etc.
                        // We remember the order of the corresponding principal key values in the _sourceQueryParamProperties
                        // field.
                        AliasGenerator keyParamNameGen = new AliasGenerator(_entityKeyParamName); // Aliases are cached in AliasGenerator
                        _sourceQueryParamProperties = principalProps;
 
                        for (int idx = 0; idx < dependentProps.Count; idx++)
                        {
                            if (idx > 0)
                            {
                                sourceBuilder.Append(" AND ");
                            }
 
                            sourceBuilder.Append("D.[");
                            sourceBuilder.Append(dependentProps[idx].Name);
                            sourceBuilder.Append("] = @");
                            sourceBuilder.Append(keyParamNameGen.Next());
                        }
                    }
                    else
                    {
                        // This related end goes from 'dependent' to 'principal', and has the key of the dependent
                        // In this case it is necessary to filter the target (principal) entity set on the foreign 
                        // key relationship properties to retrieve the corresponding principal entity.
                        //
                        // SELECT VALUE P FROM
                        //   OfType(##PrincipalEntityset, ##PrincipalEntityType) AS P
                        // WHERE 
                        //   P.PrincipalProperty1 = @DependentProperty1 AND ...
                        //
                        Debug.Assert(fkConstraint.FromRole.EdmEquals(_toEndProperty), "Source query for foreign key association related end is not based on principal or dependent?");
 
                        sourceBuilder = new StringBuilder("SELECT VALUE P FROM ");
                        AppendEntitySet(sourceBuilder, targetEntitySet, targetEntityType, ofTypeRequired);
                        sourceBuilder.Append(" AS P WHERE ");
 
                        AliasGenerator keyParamNameGen = new AliasGenerator(_entityKeyParamName); // Aliases are cached in AliasGenerator
                        _sourceQueryParamProperties = dependentProps;
                        for (int idx = 0; idx < principalProps.Count; idx++)
                        {
                            if (idx > 0)
                            {
                                sourceBuilder.Append(" AND ");
                            }
                            sourceBuilder.Append("P.[");
                            sourceBuilder.Append(principalProps[idx].Name);
                            sourceBuilder.Append("] = @");
                            sourceBuilder.Append(keyParamNameGen.Next());
                        }
                        _sourceQuery = sourceBuilder.ToString();
                    }
                }
                else
                {
                    // 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.
 
                    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("]) = ");
 
                    AppendKeyParameterRow(sourceBuilder, key.GetEntitySet(ObjectContext.MetadataWorkspace).ElementType.KeyMembers);
 
                    sourceBuilder.Append(") AS [AssociationEntry] INNER JOIN ");
 
                    AppendEntitySet(sourceBuilder, targetEntitySet, targetEntityType, ofTypeRequired);
 
                    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 specified merge option.
            ObjectQuery<TEntity> query = new ObjectQuery<TEntity>(_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
            IEnumerable<EdmMember> parameterMembers = _sourceQueryParamProperties
                ?? key.GetEntitySet(ObjectContext.MetadataWorkspace).ElementType.KeyMembers;
 
            hasResults = true;
            foreach (EdmMember parameterMember in parameterMembers)
            {
                // Create a new ObjectParameter with the next parameter name and the next entity value.
                // When _sourceQueryParamProperties are defined, it means we are handling a foreign key association. For an FK association,
                // the current entity values are considered truth. Otherwise, we use EntityKey values for backwards
                // compatibility with independent association behaviors in .NET 3.5.
                object value;
                if (null == _sourceQueryParamProperties)
                {
                    // retrieve the value from the entity key (independent association lookup)
                    value = _wrappedOwner.EntityKey.EntityKeyValues.Single(ekv => ekv.Key == parameterMember.Name).Value;
                }
                else
                {
                    // retrieve the value from the entity itself (FK lookup)
                    EntityReference reference = this as EntityReference;
                    if (reference != null && ForeignKeyFactory.IsConceptualNullKey(reference.CachedForeignKey))
                    {
                        value = null;
                    }
                    else
                    {
                        value = GetCurrentValueFromEntity(parameterMember);
                    }
                }
                ObjectParameter queryParam;
                if (null == value)
                {
                    var parameterEdmType = parameterMember.TypeUsage.EdmType;
                    Debug.Assert(Helper.IsScalarType(parameterEdmType), "Only primitive or enum type expected for parameters");
 
                    Type parameterClrType = Helper.IsPrimitiveType(parameterEdmType) ? 
                        ((PrimitiveType)parameterEdmType).ClrEquivalentType :
                        ((ClrEnumType)ObjectContext.MetadataWorkspace.GetObjectSpaceType((EnumType)parameterEdmType)).ClrType;
 
                    queryParam = new ObjectParameter(paramNameGen.Next(), parameterClrType);
                    // If any lookup value is null, the query cannot match any rows.
                    hasResults = false;
                }
                else
                {
                    queryParam = new ObjectParameter(paramNameGen.Next(), value);
                }
 
                // Map the type of the key member to C-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(parameterMember);
 
                // 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;
        }
 
        private object GetCurrentValueFromEntity(EdmMember member)
        {
            // retrieve member accessor from the object context (which already keeps track of the relevant
            // metadata)
            StateManagerTypeMetadata metaType = _context.ObjectStateManager.GetOrAddStateManagerTypeMetadata(member.DeclaringType);
            StateManagerMemberMetadata metaMember = metaType.Member(metaType.GetOrdinalforCLayerMemberName(member.Name));
            return metaMember.GetValue(_wrappedOwner.Entity);
        }
 
        private static void AppendKeyParameterRow(StringBuilder sourceBuilder, IList<EdmMember> keyMembers)
        {
            sourceBuilder.Append("ROW(");
            AliasGenerator keyParamNameGen = new AliasGenerator(_entityKeyParamName); // Aliases are cached in AliasGenerator
            int keyMemberCount = 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(")");
        }
 
        private static void AppendEntitySet(StringBuilder sourceBuilder, EntitySet targetEntitySet, EntityType targetEntityType, bool ofTypeRequired)
        {
            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("])");
            }
        }
 
        /// <summary>
        /// Validates that a call to Load has the correct conditions
        /// This helps to reduce the complexity of the Load call (SQLBU 524128)
        /// </summary>
        /// <returns>See RelatedEnd.CreateSourceQuery method. This is returned here so we can create it and validate the state before returning it to the caller</returns>
        internal ObjectQuery<TEntity> ValidateLoad<TEntity>(MergeOption mergeOption, string relatedEndName, out bool hasResults)
        {
            ObjectQuery<TEntity> sourceQuery = CreateSourceQuery<TEntity>(mergeOption, out hasResults);
            if (null == sourceQuery)
            {
                throw EntityUtil.RelatedEndNotAttachedToContext(relatedEndName);
            }
 
            EntityEntry entry = ObjectContext.ObjectStateManager.FindEntityEntry(_wrappedOwner.Entity);
            //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
        // -------
 
        /// <summary>
        /// Loads the related entity or entities into the local related end using the default merge option.        
        /// </summary>
        public void Load()
        {
            CheckOwnerNull();
            Load(DefaultMergeOption);
        }
 
        /// <summary>
        /// Loads the related entity or entities into the local related end using the supplied MergeOption.        
        /// </summary>
        public abstract void Load(MergeOption mergeOption);
 
        /// <summary>
        /// 
        /// </summary>
        internal void DeferredLoad()
        {
            if (_wrappedOwner != null && _wrappedOwner != EntityWrapperFactory.NullWrapper &&
                !IsLoaded &&
                _context != null &&
                _context.ContextOptions.LazyLoadingEnabled &&
                !_context.InMaterialization &&
                CanDeferredLoad)
            {
                // Ensure the parent EntityState is NoTracking, Unchanged, or Modified
                // Detached, Added, and Deleted parents cannot call Load
                Debug.Assert(_wrappedOwner != null, "Wrapper owner should never be null");
                if (UsingNoTracking ||
                    (_wrappedOwner.ObjectStateEntry != null &&
                     (_wrappedOwner.ObjectStateEntry.State == EntityState.Unchanged ||
                      _wrappedOwner.ObjectStateEntry.State == EntityState.Modified ||
                      (_wrappedOwner.ObjectStateEntry.State == EntityState.Added && IsForeignKey && IsDependentEndOfReferentialConstraint(false)))))
                {
                    // Avoid infinite recursive calls
                    _context.ContextOptions.LazyLoadingEnabled = false;
                    try
                    {
                        Load();
                    }
                    finally
                    {
                        _context.ContextOptions.LazyLoadingEnabled = true;
                    }
                }
            }
        }
 
        internal virtual bool CanDeferredLoad
        {
            get { return true; }
        }
 
        /// <summary>
        /// Takes a list of related entities and merges them into the current collection.
        /// </summary>        
        /// <param name="collection">Entities to relate to the owner of this EntityCollection</param>
        /// <param name="mergeOption">MergeOption to use when updating existing relationships</param>
        /// <param name="setIsLoaded">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.</param>
        internal void Merge<TEntity>(IEnumerable<TEntity> collection, MergeOption mergeOption, bool setIsLoaded)
        {
            List<IEntityWrapper> refreshedCollection = collection as List<IEntityWrapper>;
            if (refreshedCollection == null)
            {
                refreshedCollection = new List<IEntityWrapper>();
                EntitySet targetEntitySet = ((AssociationSet)RelationshipSet).AssociationSetEnds[TargetRoleName].EntitySet;
                foreach (TEntity entity in collection)
                {
                    IEntityWrapper wrapper = EntityWrapperFactory.WrapEntityUsingContext(entity, ObjectContext);
                    // When the MergeOption is NoTraking, we need to make sure the wrapper reflects the current context and
                    // has an EntityKey
                    if (mergeOption == MergeOption.NoTracking)
                    {
                        EntityWrapperFactory.UpdateNoTrackingWrapper(wrapper, ObjectContext, targetEntitySet);
                    }
                    refreshedCollection.Add(wrapper);
                }
            }
            Merge<TEntity>(refreshedCollection, mergeOption, setIsLoaded);
        }
 
        // Internal version of Merge that works on wrapped entities.
        internal void Merge<TEntity>(List<IEntityWrapper> 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
            EntityKey sourceKey = _wrappedOwner.EntityKey;
            EntityUtil.CheckEntityKeyNull(sourceKey);
 
            ObjectStateManager.UpdateRelationships(this.ObjectContext, mergeOption, (AssociationSet)RelationshipSet, (AssociationEndMember)FromEndProperty, sourceKey, _wrappedOwner, (AssociationEndMember)ToEndMember, collection, setIsLoaded);
 
            if (setIsLoaded)
            {
                // If the input collection contains all related entities, mark the collection as "loaded"
                _isLoaded = true;
            }
        }
 
        /// <summary>
        /// Attaches an entity to the related end.  This method works in exactly the same way as Attach(object).
        /// It is maintained for backward compatibility with previous versions of IRelatedEnd.
        /// </summary>
        /// <param name="entity">The entity to attach to the related end</param>
        /// <exception cref="ArgumentNullException">Thrown when <paramref name="entity"/> is null.</exception>
        /// <exception cref="InvalidOperationException">Thrown when the entity cannot be related via the current relationship end.</exception>
        void IRelatedEnd.Attach(IEntityWithRelationships entity)
        {
            ((IRelatedEnd)this).Attach((object)entity);
        }
 
        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="entity">The entity to attach to the related end</param>
        /// <exception cref="ArgumentNullException">Thrown when <paramref name="entity"/> is null.</exception>
        /// <exception cref="InvalidOperationException">Thrown when the entity cannot be related via the current relationship end.</exception>
        void IRelatedEnd.Attach(object entity)
        {
            CheckOwnerNull();
            EntityUtil.CheckArgumentNull(entity, "entity");
            Attach(new IEntityWrapper[] { EntityWrapperFactory.WrapEntityUsingContext(entity, ObjectContext) }, false);
        }
 
        internal void Attach(IEnumerable<IEntityWrapper> wrappedEntities, bool allowCollection)
        {
            CheckOwnerNull();
            ValidateOwnerForAttach();
 
            // validate children and collect them in the "refreshedCollection" for this instance
            int index = 0;
            List<IEntityWrapper> collection = new List<IEntityWrapper>();
 
            foreach (IEntityWrapper entity in wrappedEntities)
            {
                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*/);
                ReferentialConstraint constraint = ((AssociationType)RelationMetadata).ReferentialConstraints.FirstOrDefault();
                if (constraint != null)
                {
                    ObjectStateManager stateManager = ObjectContext.ObjectStateManager;
                    EntityEntry ownerEntry = stateManager.FindEntityEntry(_wrappedOwner.Entity);
                    Debug.Assert(ownerEntry != null, "Both entities should be attached.");
                    if (IsDependentEndOfReferentialConstraint(checkIdentifying: false))
                    {
                        Debug.Assert(collection.Count == 1, "Dependant should attach to single principal");
                        if (!VerifyRIConstraintsWithRelatedEntry(constraint, ownerEntry.GetCurrentEntityValue, collection[0].ObjectStateEntry.EntityKey))
                        {
                            throw EntityUtil.InconsistentReferentialConstraintProperties();
                        }
                    }
                    else
                    {
                        foreach (IEntityWrapper wrappedTarget in collection)
                        {
                            RelatedEnd targetRelatedEnd = GetOtherEndOfRelationship(wrappedTarget);
                            if (targetRelatedEnd.IsDependentEndOfReferentialConstraint(checkIdentifying: false))
                            {
                                EntityEntry targetEntry = stateManager.FindEntityEntry(((EntityReference)targetRelatedEnd).WrappedOwner.Entity);
                                Debug.Assert(targetEntry != null, "Both entities should be attached.");
                                if (!VerifyRIConstraintsWithRelatedEntry(constraint, targetEntry.GetCurrentEntityValue, ownerEntry.EntityKey))
                                {
                                    throw EntityUtil.InconsistentReferentialConstraintProperties();
                                }
                            }
                        }
                    }
                }
            }
            finally
            {
                _suppressEvents = false;
            }
            OnAssociationChanged(CollectionChangeAction.Refresh, null);
        }
 
        // verifies requirements for Owner in Attach()
        internal void ValidateOwnerForAttach()
        {
            if (null == this.ObjectContext || UsingNoTracking)
            {
                throw EntityUtil.InvalidOwnerStateForAttach();
            }
 
            // find state entry
            EntityEntry stateEntry = this.ObjectContext.ObjectStateManager.GetEntityEntry(_wrappedOwner.Entity);
            if (stateEntry.State != EntityState.Modified &&
                stateEntry.State != EntityState.Unchanged)
            {
                throw EntityUtil.InvalidOwnerStateForAttach();
            }
        }
 
        // verifies requirements for child entity passed to Attach()
        internal void ValidateEntityForAttach(IEntityWrapper wrappedEntity, int index, bool allowCollection)
        {
            if (null == wrappedEntity || null == wrappedEntity.Entity)
            {
                if (allowCollection)
                {
                    throw EntityUtil.InvalidNthElementNullForAttach(index);
                }
                else
                {
                    throw EntityUtil.ArgumentNull("wrappedEntity");
                }
            }
 
            // Having this verification here results in having the same exception no matter how the further code path is changed.
            this.VerifyType(wrappedEntity);
 
            // 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.");
            EntityEntry stateEntry = ObjectContext.ObjectStateManager.FindEntityEntry(wrappedEntity.Entity);
            if (null == stateEntry || !Object.ReferenceEquals(stateEntry.Entity, wrappedEntity.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();
 
        /// <summary>
        /// Adds an entity to the related end.  This method works in exactly the same way as Add(object).
        /// It is maintained for backward compatibility with previous versions of IRelatedEnd.
        /// </summary>
        /// <param name="entity">
        ///   Entity instance to add to the related end
        /// </param>
        void IRelatedEnd.Add(IEntityWithRelationships entity)
        {
            ((IRelatedEnd)this).Add((object)entity);
        }
 
        /// <summary>
        ///   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.
        /// </summary>
        /// <param name="entity">
        ///   Entity instance to add to the related end
        /// </param>
        void IRelatedEnd.Add(object entity)
        {
            EntityUtil.CheckArgumentNull(entity, "entity");
            this.Add(EntityWrapperFactory.WrapEntityUsingContext(entity, ObjectContext));
        }
 
        internal void Add(IEntityWrapper wrappedEntity)
        {
            Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
 
            if (_wrappedOwner.Entity != null)
            {
                Add(wrappedEntity, /*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(wrappedEntity);
            }
        }
 
        /// <summary>
        /// Removes an entity from the related end.  This method works in exactly the same way as Remove(object).
        /// It is maintained for backward compatibility with previous versions of IRelatedEnd.
        /// </summary>
        /// <param name="entity">
        ///   Entity instance to remove from the related end
        /// </param>
        /// <returns>Returns true if the entity was successfully removed, false if the entity was not part of the RelatedEnd.</returns>
        bool IRelatedEnd.Remove(IEntityWithRelationships entity)
        {
            return ((IRelatedEnd)this).Remove((object)entity);
        }
 
        /// <summary>
        ///   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.
        /// </summary>
        /// <param name="entity">
        ///   Entity instance to remove from the related end
        /// </param>
        /// <returns>Returns true if the entity was successfully removed, false if the entity was not part of the RelatedEnd.</returns>
        bool IRelatedEnd.Remove(object entity)
        {
            EntityUtil.CheckArgumentNull(entity, "entity");
            DeferredLoad();
            return Remove(EntityWrapperFactory.WrapEntityUsingContext(entity, ObjectContext), false);
        }
 
        // Internal version that works on a wrapped entity and can be called from multiple
        // places where the public version is no longer appropriate.
        internal bool Remove(IEntityWrapper wrappedEntity, bool preserveForeignKey)
        {
            Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
 
            if (_wrappedOwner.Entity != null)
            {
                if (ContainsEntity(wrappedEntity))
                {
 
                    Remove(wrappedEntity, /*fixup*/true, /*deleteEntity*/false, /*deleteOwner*/false, /*applyReferentialConstraints*/true, preserveForeignKey);
                    return true;
                }
                // The entity is not related so return false
                return false;
 
            }
            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
                return DisconnectedRemove(wrappedEntity);
            }
        }
 
 
        internal abstract void DisconnectedAdd(IEntityWrapper wrappedEntity);
        internal abstract bool DisconnectedRemove(IEntityWrapper wrappedEntity);
 
        internal void Add(IEntityWrapper wrappedEntity, bool applyConstraints)
        {
            // SQLBU: 508819 508813 508752
            // Detect as soon as possible if we are trying to re-add entities which are in Deleted state.
            // When one of the entity is in Deleted state, attempt would be made to re-add 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 interested only in Deleted entities)
            if (_context != null && !UsingNoTracking)
            {
                ValidateStateForAdd(_wrappedOwner);
                ValidateStateForAdd(wrappedEntity);
            }
 
            this.Add(wrappedEntity,
                applyConstraints: applyConstraints,
                addRelationshipAsUnchanged: false,
                relationshipAlreadyExists: false,
                allowModifyingOtherEndOfRelationship: true,
                forceForeignKeyChanges: true);
 
        }
 
        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(IEntityWrapper wrappedEntity)
        {
            Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
            EntityEntry entry = this.ObjectContext.ObjectStateManager.FindEntityEntry(wrappedEntity.Entity);
            if (entry != null && entry.State == EntityState.Deleted)
            {
                throw EntityUtil.UnableToAddRelationshipWithDeletedEntity();
            }
        }
 
        internal void Add(IEntityWrapper wrappedTarget,
            bool applyConstraints,
            bool addRelationshipAsUnchanged,
            bool relationshipAlreadyExists,
            bool allowModifyingOtherEndOfRelationship,   // needed by ChangeRelationshipState - check multiplicity constraints instead of silently updating other end of relationship
            bool forceForeignKeyChanges)
        {
            Debug.Assert(wrappedTarget != null, "IEntityWrapper instance is null.");
            // Do verification
            if (!this.VerifyEntityForAdd(wrappedTarget, relationshipAlreadyExists))
            {
                // Allow the same item to be "added" to a collection as a no-op operation
                return;
            }
 
            EntityKey key = wrappedTarget.EntityKey;
            if ((object)key != null && ObjectContext != null)
            {
                CheckRelationEntitySet(key.GetEntitySet(ObjectContext.MetadataWorkspace));
            }
 
            RelatedEnd targetRelatedEnd = GetOtherEndOfRelationship(wrappedTarget);
 
            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
                    wrappedTarget.ResetContext(this.ObjectContext, GetTargetEntitySetFromRelationshipSet(), MergeOption.NoTracking);
                }
                else
                {
                    // Both entities are already tracked by different non-null contexts
                    throw EntityUtil.CannotCreateRelationshipEntitiesInDifferentContexts();
                }
            }
            else if ((_context == null || UsingNoTracking) && (targetRelatedEnd.ObjectContext != null && !targetRelatedEnd.UsingNoTracking))
            {
                // Only the target has a context, so validate it is in a suitable state
                targetRelatedEnd.ValidateStateForAdd(targetRelatedEnd.WrappedOwner);
            }
 
            targetRelatedEnd.VerifyEntityForAdd(_wrappedOwner, relationshipAlreadyExists);
 
 
            // Do actual add
 
            // Perform multiplicity constraints verification for the target related end before current related end is modified.
            // The "allowModifyingOtherEndOfRelationship" is used by ObjectStateManager.ChangeRelationshipState.
            targetRelatedEnd.VerifyMultiplicityConstraintsForAdd(!allowModifyingOtherEndOfRelationship);
 
            // Add the target entity to the source entity's collection or reference
            if (this.CheckIfNavigationPropertyContainsEntity(wrappedTarget))
            {
                this.AddToLocalCache(wrappedTarget, applyConstraints);
            }
            else
            {
                this.AddToCache(wrappedTarget, 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 be always false to enable scenarios like this:
            //             orderLine.Order = order1;
            //             order2.OrderLines.Add(orderLine); // orderLine.Order is changed to order2
            if (targetRelatedEnd.CheckIfNavigationPropertyContainsEntity(WrappedOwner))
            {
                // Example: IPOCO order, POCO customer with a bidirectional relationship
                //  customer.Orders.Add(order);
                //  order.Customer = customer <-- the Orders collection already contains "order" on fixup and this would add a duplicate
                targetRelatedEnd.AddToLocalCache(_wrappedOwner, /*applyConstraints*/ false);
            }
            else
            {
                targetRelatedEnd.AddToCache(_wrappedOwner, /*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.
            IEntityWrapper entityToAdd = null; // the entity to be added to attachedRelatedEnd
 
            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 (!this.IsForeignKey && !relationshipAlreadyExists && !UsingNoTracking)
                {
                    // If this Add is triggered by setting the principle end of an unchanged/modified dependent end, then the relationship should be Unchanged
                    if (!this.ObjectContext.ObjectStateManager.TransactionManager.IsLocalPublicAPI &&
                        this.WrappedOwner.EntityKey != null &&
                        !this.WrappedOwner.EntityKey.IsTemporary && IsDependentEndOfReferentialConstraint(false))
                    {
                        addRelationshipAsUnchanged = true;
                    }
 
                    AddRelationshipToObjectStateManager(wrappedTarget, addRelationshipAsUnchanged, /*doAttach*/false);
                }
 
                // The condition (IsAddTracking || IsAttachTracking || IsDetectChanges) excludes the case
                // when the method is called from materialization when we don't want to verify the navigation property.
                if (wrappedTarget.RequiresRelationshipChangeTracking &&
                    (this.ObjectContext.ObjectStateManager.TransactionManager.IsAddTracking ||
                     this.ObjectContext.ObjectStateManager.TransactionManager.IsAttachTracking ||
                     this.ObjectContext.ObjectStateManager.TransactionManager.IsDetectChanges))
                {
                    this.AddToNavigationProperty(wrappedTarget);
                    targetRelatedEnd.AddToNavigationProperty(this._wrappedOwner);
                }
            }
            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 = _wrappedOwner;
                }
                else
                {
                    attachedRelatedEnd = this;
                    entityToAdd = wrappedTarget;
                }
 
                if (!attachedRelatedEnd.UsingNoTracking)
                {
                    TransactionManager transManager = attachedRelatedEnd.WrappedOwner.Context.ObjectStateManager.TransactionManager;
                    transManager.BeginAddTracking();
 
                    try
                    {
                        bool doCleanup = true;
 
                        try
                        {
                            if (attachedRelatedEnd.WrappedOwner.Context.ObjectStateManager.TransactionManager.TrackProcessedEntities)
                            {
                                // The Entity could have been already wrapped by DetectChanges
                                if (!attachedRelatedEnd.WrappedOwner.Context.ObjectStateManager.TransactionManager.WrappedEntities.ContainsKey(entityToAdd.Entity))
                                {
                                    attachedRelatedEnd.WrappedOwner.Context.ObjectStateManager.TransactionManager.WrappedEntities.Add(entityToAdd.Entity, entityToAdd);
                                }
                                attachedRelatedEnd.WrappedOwner.Context.ObjectStateManager.TransactionManager.ProcessedEntities.Add(attachedRelatedEnd.WrappedOwner);
                            }
 
                            attachedRelatedEnd.AddGraphToObjectStateManager(entityToAdd, relationshipAlreadyExists,
                                addRelationshipAsUnchanged, /*doAttach*/ false);
 
                            // The code below is almost duplicated as the code few lines above 
                            // because this one can't be moved outside of try{}catch{}.
                            if (entityToAdd.RequiresRelationshipChangeTracking && TargetAccessor.HasProperty)
                            {
                                Debug.Assert(this.CheckIfNavigationPropertyContainsEntity(wrappedTarget), "owner's navigation property doesn't contain the target entity as expected");
                                targetRelatedEnd.AddToNavigationProperty(this._wrappedOwner);
                            }
 
                            doCleanup = false;
                        }
                        finally
                        {
                            if (doCleanup)
                            {
                                Debug.Assert(entityToAdd != null, "entityToAdd should be set if attachedRelatedEnd is set");
 
                                attachedRelatedEnd.WrappedOwner.Context.ObjectStateManager.DegradePromotedRelationships();
 
                                // Remove the source entity from the target related end
                                attachedRelatedEnd.FixupOtherEndOfRelationshipForRemove(entityToAdd, /*preserveForeignKey*/ false);
 
                                // Remove the target entity from the source related end
                                attachedRelatedEnd.RemoveFromCache(entityToAdd, /*resetIsLoaded*/ false, /*preserveForeignKey*/ false);
 
                                // Remove the graph that we just tried to add to the context
                                entityToAdd.RelationshipManager.NodeVisited = true;
                                RelationshipManager.RemoveRelatedEntitiesFromObjectStateManager(entityToAdd);
                                RemoveEntityFromObjectStateManager(entityToAdd);
                            }
                        }
                    }
                    finally
                    {
                        attachedRelatedEnd.WrappedOwner.Context.ObjectStateManager.TransactionManager.EndAddTracking();
                    }
                }
            }
 
            // FK: update foreign key values on the dependent end.
            if (this.ObjectContext != null &&
                this.IsForeignKey &&
                !this.ObjectContext.ObjectStateManager.TransactionManager.IsGraphUpdate)
            {
                // Note that we use "forceChange" below so that the FK properties will be set as modified
                // even if they don't actually change.
                if (this.IsDependentEndOfReferentialConstraint(false))
                {
                    Debug.Assert(this is EntityReference, "Dependent end cannot be a collection.");
                    ((EntityReference)this).UpdateForeignKeyValues(_wrappedOwner, wrappedTarget, changedFKs: null, forceChange: forceForeignKeyChanges);
                }
                else if (targetRelatedEnd.IsDependentEndOfReferentialConstraint(false))
                {
                    Debug.Assert(targetRelatedEnd is EntityReference, "Dependent end cannot be a collection.");
                    ((EntityReference)targetRelatedEnd).UpdateForeignKeyValues(wrappedTarget, _wrappedOwner, changedFKs: null, forceChange: forceForeignKeyChanges);
                }
            }
 
            // 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, _wrappedOwner.Entity);
            OnAssociationChanged(CollectionChangeAction.Add, wrappedTarget.Entity);
        }
 
        private void AddGraphToObjectStateManager(IEntityWrapper wrappedEntity, bool relationshipAlreadyExists,
                                                  bool addRelationshipAsUnchanged, bool doAttach)
        {
            Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
            Debug.Assert(!UsingNoTracking, "Should not be attempting to add graphs to the state manager with NoTracking related ends");
 
            AddEntityToObjectStateManager(wrappedEntity, doAttach);
            if (!relationshipAlreadyExists &&
                this.ObjectContext != null && wrappedEntity.Context != null)
            {
                if (!this.IsForeignKey)
                {
                    AddRelationshipToObjectStateManager(wrappedEntity, addRelationshipAsUnchanged, doAttach);
                }
 
                if (wrappedEntity.RequiresRelationshipChangeTracking || WrappedOwner.RequiresRelationshipChangeTracking)
                {
                    this.UpdateSnapshotOfRelationships(wrappedEntity);
                    if (doAttach)
                    {
                        EntityEntry entry = _context.ObjectStateManager.GetEntityEntry(wrappedEntity.Entity);
                        wrappedEntity.RelationshipManager.CheckReferentialConstraintProperties(entry);
                    }
                }
            }
            WalkObjectGraphToIncludeAllRelatedEntities(wrappedEntity, addRelationshipAsUnchanged, doAttach);
        }
 
        private void UpdateSnapshotOfRelationships(IEntityWrapper wrappedEntity)
        {
            RelatedEnd otherRelatedEnd = this.GetOtherEndOfRelationship(wrappedEntity);
            if (!otherRelatedEnd.ContainsEntity(this.WrappedOwner))
            {
                // Since we now align changes, we can allow the Add to remove the old value
                // Reference/FK violations are detected elsewhere
                otherRelatedEnd.AddToLocalCache(this.WrappedOwner, applyConstraints: false);
            }
        }
 
        internal void Remove(IEntityWrapper wrappedEntity, bool doFixup, bool deleteEntity, bool deleteOwner, bool applyReferentialConstraints, bool preserveForeignKey)
        {
            if (wrappedEntity.RequiresRelationshipChangeTracking &&      // Is it POCO?
                doFixup &&                                               // Remove() is called for both ends of relationship, once with doFixup==true, once with doFixup==false. Verify only one time.
                this.TargetAccessor.HasProperty)                     // Is there anything to verify?
            {
                bool contains = this.CheckIfNavigationPropertyContainsEntity(wrappedEntity);
 
                if (!contains)
                {
                    RelatedEnd relatedEnd = GetOtherEndOfRelationship(wrappedEntity);
                    relatedEnd.RemoveFromNavigationProperty(this.WrappedOwner);
                }
            }
 
            if (!this.ContainsEntity(wrappedEntity))
            {
                return;
            }
 
            // There can be a case when symmetrical Remove() shall be performed because of Referential Constraints
            // Example:
            //   Relationship 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(false))  // don't check the nullability of the "from" properties
            {
                // Remove _wrappedOwner from the related end with applying Referential Constraints
                RelatedEnd relatedEnd = GetOtherEndOfRelationship(wrappedEntity);
                relatedEnd.Remove(_wrappedOwner, doFixup, deleteEntity, deleteOwner, applyReferentialConstraints, preserveForeignKey);
 
                return;
            }
 
 
            //The following call will verify that the given entity is part of the collection or ref.
            bool fireEvent = RemoveFromCache(wrappedEntity, false, preserveForeignKey);
 
            if (!UsingNoTracking &&
                this.ObjectContext != null &&
                !this.IsForeignKey)
            {
                MarkRelationshipAsDeletedInObjectStateManager(wrappedEntity, _wrappedOwner, _relationshipSet, _navigation);
            }
 
            if (doFixup)
            {
                FixupOtherEndOfRelationshipForRemove(wrappedEntity, preserveForeignKey);
 
                // For the "LocalPublicAPI" just remove the entity from the related end, don't trigger cascade delete
                if (_context == null || !_context.ObjectStateManager.TransactionManager.IsLocalPublicAPI)
                {
                    //The related end "entity" cannot live without this side "owner". It should be deleted. Cascade this 
                    // effect to related entities of the "related" entity
                    // We skip this delete/detach if the entity is being reparented (TransactionManager.EntityBeingReparented)
                    // or if the reference is being nulled as part of fixup in a POCO proxy while setting the FK (InFKSetter).
                    if (null != _context && (deleteEntity ||
                       (deleteOwner && CheckCascadeDeleteFlag(_fromEndProperty)) ||
                       (applyReferentialConstraints && IsPrincipalEndOfReferentialConstraint())) &&
                        !ReferenceEquals(wrappedEntity.Entity, _context.ObjectStateManager.TransactionManager.EntityBeingReparented) &&
                        !ReferenceEquals(_context.ObjectStateManager.EntityInvokingFKSetter, wrappedEntity.Entity))
                    {
                        //Once related entity is deleted, all relationships involving related entity would be updated
 
                        // RemoveEntityFromRelatedEnds check for graph circularities to make sure
                        // it does not get into infinite loop
                        EnsureRelationshipNavigationAccessorsInitialized();
                        RemoveEntityFromRelatedEnds(wrappedEntity, _wrappedOwner, _navigation.Reverse);
                        MarkEntityAsDeletedInObjectStateManager(wrappedEntity);
                    }
                }
            }
 
            if (fireEvent)
            {
                OnAssociationChanged(CollectionChangeAction.Remove, wrappedEntity.Entity);
            }
        }
 
 
        /// <summary>
        /// Returns true if this Related end represents the dependent of a Referential Constraint
        /// </summary>
        /// <param name="checkIdentifying">If true then the method will only return true if the Referential Constraint is identifying</param>
        internal bool IsDependentEndOfReferentialConstraint(bool checkIdentifying)
        {
            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)this.RelationMetadata).ReferentialConstraints)
                {
                    if (constraint.ToRole == this.FromEndProperty)
                    {
                        if (checkIdentifying)
                        {
                            EntityType entityType = constraint.ToRole.GetEntityType();
                            bool allPropertiesAreKeyProperties = RelatedEnd.CheckIfAllPropertiesAreKeyProperties(entityType.KeyMemberNames, constraint.ToProperties);
 
                            return allPropertiesAreKeyProperties;
                        }
                        else
                        {
                            // Example: 
                            //    Client<C_ID> --- Order<O_ID, Client_ID>
                            //    RI Constraint: Principal/From <Client.C_ID>,  Dependent/To <Order.Client_ID>
                            // When current RelatedEnd is a CollectionOrReference in Order's relationships,
                            // constarint.ToRole == this._fromEndProperty == Order
                            return true;
                        }
                    }
                }
            }
            return false;
        }
 
        /// <summary>
        /// Check if current RelatedEnd is a Principal end of some Referential Constraint and if some of the "from" properties is not-nullable
        /// </summary>
        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)
                    {
                        EntityType entityType = constraint.ToRole.GetEntityType();
                        bool allPropertiesAreKeyProperties = RelatedEnd.CheckIfAllPropertiesAreKeyProperties(entityType.KeyMemberNames, constraint.ToProperties);
 
                        // Example: 
                        //    Client<C_ID> --- Order<O_ID, Client_ID>
                        //    RI Constraint: Principal/From <Client.C_ID>,  Dependent/To <Order.Client_ID>
                        // When current RelatedEnd is a CollectionOrReference in Client's relationships,
                        // constarint.FromRole == this._fromEndProperty == Client
                        return allPropertiesAreKeyProperties;
                    }
                }
            }
            return false;
        }
 
        static internal bool CheckIfAllPropertiesAreKeyProperties(string[] keyMemberNames, ReadOnlyMetadataCollection<EdmProperty> toProperties)
        {
            // Check if some of the "to" properties is not a key property
            foreach (EdmProperty property in toProperties)
            {
                bool found = false;
                foreach (string keyPropertyName in keyMemberNames)
                {
                    if (keyPropertyName == property.Name)
                    {
                        found = true;
                        break;
                    }
                }
                if (!found)
                {
                    return false;
                }
            }
            return true;
        }
 
        //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(IEntityWrapper wrappedEntity, bool addRelationshipAsUnchanged, bool doAttach)
        {
            Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
            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
            EntityEntry cacheEntry = _context.ObjectStateManager.FindEntityEntry(wrappedEntity.Entity);
            Debug.Assert(cacheEntry == null || cacheEntry.Entity == wrappedEntity.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)
            {
                throw EntityUtil.UnableToAddRelationshipWithDeletedEntity();
            }
 
            if (wrappedEntity.RequiresRelationshipChangeTracking || WrappedOwner.RequiresRelationshipChangeTracking)
            {
                // Verify relationship fixup before including rest of the graph.
                RelatedEnd otherRelatedEnd = GetOtherEndOfRelationship(wrappedEntity);
 
                // Validate the type is compatible before trying to get/set properties on it.
                // The following will throw if the type is not mapped.
                _context.GetTypeUsage(otherRelatedEnd.WrappedOwner.IdentityType);
 
                // If the other end is a reference that is non-null, then don't overwrite it.
                // If the reference is non-null and doesn't match what we think it should be, then throw.
                EntityReference otherEndAsRef = otherRelatedEnd as EntityReference;
                if (otherEndAsRef != null)
                {
                    if (otherEndAsRef.NavigationPropertyIsNullOrMissing())
                    {
                        otherRelatedEnd.AddToNavigationProperty(this._wrappedOwner);
                        // If the other end is a dependent that is already tracked, then we need to make sure
                        // its FK props are marked as modified even though we are not fixing them up.
                        Debug.Assert(ObjectContext != null, "Expected attached context at this point.");
                        if (cacheEntry != null && 
                            ObjectContext.ObjectStateManager.TransactionManager.IsAddTracking &&
                            IsForeignKey &&
                            otherRelatedEnd.IsDependentEndOfReferentialConstraint(checkIdentifying: false))
                        {
                            otherRelatedEnd.MarkForeignKeyPropertiesModified();
                        }
                    }
                    else if (!otherEndAsRef.CheckIfNavigationPropertyContainsEntity(this._wrappedOwner))
                    {
                        throw new InvalidOperationException(System.Data.Entity.Strings.ObjectStateManager_ConflictingChangesOfRelationshipDetected(
                            otherEndAsRef.RelationshipNavigation.To,
                            otherEndAsRef.RelationshipNavigation.RelationshipName));
                    }
                }
                else
                {
                    // For collections, always add
                    otherRelatedEnd.AddToNavigationProperty(this._wrappedOwner);
                }
            }
 
            if (null == cacheEntry)
            {
                // 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(wrappedEntity, /*relationshipAlreadyExists*/ false,
                                             addRelationshipAsUnchanged, doAttach);
            }
 
            // 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(wrappedEntity))
            {
                // 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 = wrappedEntity.EntityKey;
                    if (entityRef.DetachedEntityKey != targetKey)
                    {
                        // Check for the case where a NoTracking (with detached entity key) is being Added and throw the same
                        // exception we do elsewhere for this case.
                        // We might consider changing this behavior in the future to just put the entity in the Added state,
                        // but for consistency for now we throw the same exception as elsewhere.
                        if (targetKey.IsTemporary)
                        {
                            throw EntityUtil.CannotCreateRelationshipBetweenTrackedAndNoTrackedEntities(_navigation.To);
                        }
 
                        throw EntityUtil.EntityKeyValueMismatch();
                    }
                    // else -- null just means the key isn't set, so the target entity key doesn't also have to be null
                }
 
                if (this.ObjectContext != null && wrappedEntity.Context != null)
                {
                    if (!this.IsForeignKey)
                    {
                        if (cacheEntry.State == EntityState.Added)
                        {
                            // In POCO, when the graph is partially attached and user is calling Attach on the detached entity
                            // and the entity in the context is in the Added state, the relationship has to created also in Added state.
                            AddRelationshipToObjectStateManager(wrappedEntity, addRelationshipAsUnchanged, false);
                        }
                        else
                        {
                            AddRelationshipToObjectStateManager(wrappedEntity, addRelationshipAsUnchanged, doAttach);
                        }
                    }
 
                    if (wrappedEntity.RequiresRelationshipChangeTracking || WrappedOwner.RequiresRelationshipChangeTracking)
                    {
                        this.UpdateSnapshotOfRelationships(wrappedEntity);
                        if (doAttach && cacheEntry.State != EntityState.Added)
                        {
                            EntityEntry entry = this.ObjectContext.ObjectStateManager.GetEntityEntry(wrappedEntity.Entity);
                            wrappedEntity.RelationshipManager.CheckReferentialConstraintProperties(entry);
                        }
                    }
                }
            }
            // else relationship is already there, nothing more to do
        }
 
        internal void MarkForeignKeyPropertiesModified()
        {
            Debug.Assert(IsForeignKey, "cannot update foreign key values if the relationship is not a FK");
            ReferentialConstraint constraint = ((AssociationType)RelationMetadata).ReferentialConstraints[0];
            Debug.Assert(constraint != null, "null constraint");
 
            EntityEntry dependentEntry = WrappedOwner.ObjectStateEntry;
            Debug.Assert(dependentEntry != null, "Expected tracked entity.");
 
            // No need to try to mark properties as modified for added/deleted/detached entities.
            // Even if the entity is modified, the FK props may not be modified.
            if (dependentEntry.State == EntityState.Unchanged || dependentEntry.State == EntityState.Modified)
            {
                foreach (var dependentProp in constraint.ToProperties)
                {
                    dependentEntry.SetModifiedProperty(dependentProp.Name);
                }
            }
        }
 
        internal abstract bool CheckIfNavigationPropertyContainsEntity(IEntityWrapper wrapper);
 
        internal abstract void VerifyNavigationPropertyForAdd(IEntityWrapper wrapper);
 
        internal void AddToNavigationProperty(IEntityWrapper wrapper)
        {
            Debug.Assert(this.RelationshipNavigation != null, "null RelationshipNavigation");
 
            if (this.TargetAccessor.HasProperty && !this.CheckIfNavigationPropertyContainsEntity(wrapper))
            {
                Debug.Assert(wrapper.Context != null, "Expected context to be available.");
                // We keep track of the nav properties we have set during Add/Attach so that they
                // can be undone during rollback.
                TransactionManager tm = wrapper.Context.ObjectStateManager.TransactionManager;
                if (tm.IsAddTracking || tm.IsAttachTracking)
                {
                    wrapper.Context.ObjectStateManager.TrackPromotedRelationship(this, wrapper);
                }
                this.AddToObjectCache(wrapper);
            }
        }
 
        internal void RemoveFromNavigationProperty(IEntityWrapper wrapper)
        {
            Debug.Assert(this.RelationshipNavigation != null, "null RelationshipNavigation");
 
            if (this.TargetAccessor.HasProperty && this.CheckIfNavigationPropertyContainsEntity(wrapper))
            {
                this.RemoveFromObjectCache(wrapper);
            }
        }
 
        // Remove given entity and its relationship from ObjectStateManager.
        // Traversegraph to recursively remove all entities in the graph.
        internal void ExcludeEntity(IEntityWrapper wrappedEntity)
        {
            Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
            Debug.Assert(!UsingNoTracking, "Should not try to exclude entities from the state manager for NoTracking related ends.");
 
            if (!_context.ObjectStateManager.TransactionManager.TrackProcessedEntities ||
                !(_context.ObjectStateManager.TransactionManager.IsAttachTracking || _context.ObjectStateManager.TransactionManager.IsAddTracking) ||
                _context.ObjectStateManager.TransactionManager.ProcessedEntities.Contains(wrappedEntity))
            {
                //check to see if entity is already removed from the cache
                EntityEntry cacheEntry = _context.ObjectStateManager.FindEntityEntry(wrappedEntity.Entity);
 
                if (null != cacheEntry && cacheEntry.State != EntityState.Deleted && !wrappedEntity.RelationshipManager.NodeVisited)
                {
                    wrappedEntity.RelationshipManager.NodeVisited = true;
 
                    RelationshipManager.RemoveRelatedEntitiesFromObjectStateManager(wrappedEntity);
                    if (!this.IsForeignKey)
                    {
                        RemoveRelationshipFromObjectStateManager(wrappedEntity, _wrappedOwner, _relationshipSet, _navigation);
                    }
                    RemoveEntityFromObjectStateManager(wrappedEntity);
                }
                // 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 (!this.IsForeignKey && null != FindRelationshipEntryInObjectStateManager(wrappedEntity))
                {
                    RemoveRelationshipFromObjectStateManager(wrappedEntity, _wrappedOwner, _relationshipSet, _navigation);
                }
            }
        }
 
        internal RelationshipEntry FindRelationshipEntryInObjectStateManager(IEntityWrapper wrappedEntity)
        {
            Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
            Debug.Assert(!UsingNoTracking, "Should not look for RelationshipEntry in ObjectStateManager for NoTracking cases.");
            EntityKey entityKey = wrappedEntity.EntityKey;
            EntityKey ownerKey = _wrappedOwner.EntityKey;
            return this._context.ObjectStateManager.FindRelationship(_relationshipSet,
                new KeyValuePair<string, EntityKey>(_navigation.From, ownerKey),
                new KeyValuePair<string, EntityKey>(_navigation.To, entityKey));
        }
 
        internal void Clear(IEntityWrapper wrappedEntity, RelationshipNavigation navigation, bool doCascadeDelete)
        {
            ClearCollectionOrRef(wrappedEntity, navigation, doCascadeDelete);
        }
 
        // Check if related entities contain proper property values 
        // (entities with temporary keys are skipped)
        internal bool CheckReferentialConstraintProperties(EntityEntry ownerEntry)
        {
            // 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)
                    {
                        Debug.Assert(this is EntityReference, "Expected reference to principal");
                        EntityKey principalKey;
                        if (IsEmpty())
                        {
                            // Generally for foreign keys we want to use the EntityKey to do RI constraint validation
                            // However, if we are doing an Add/Attach, we should use the DetachedEntityKey because this is the value
                            // set by the user while the entity was detached, and should be used until the entity is fully added/attached
                            if (this.IsForeignKey &&
                                !(this.ObjectContext.ObjectStateManager.TransactionManager.IsAddTracking ||
                                  this.ObjectContext.ObjectStateManager.TransactionManager.IsAttachTracking))
                            {
                                principalKey = ((EntityReference)this).EntityKey;
                            }
                            else
                            {
                                principalKey = ((EntityReference)this).DetachedEntityKey;
                            }
                        }
                        else
                        {
                            IEntityWrapper wrappedRelatedEntity = ((EntityReference)this).ReferenceValue;
                            // For Added entities, it doesn't matter what the key value is since it can't be trusted anyway.
                            if (wrappedRelatedEntity.ObjectStateEntry != null && wrappedRelatedEntity.ObjectStateEntry.State == EntityState.Added)
                            {
                                return true;
                            }
                            principalKey = ExtractPrincipalKey(wrappedRelatedEntity);
                        }
                        if (!VerifyRIConstraintsWithRelatedEntry(constraint, ownerEntry.GetCurrentEntityValue, principalKey))
                        {
                            return false;
                        }
                    }
                    else if (constraint.FromRole == FromEndProperty)
                    {
                        if (IsEmpty())
                        {
                            // related end is empty, so we must have a reference with a detached key
                            EntityKey detachedKey = ((EntityReference)this).DetachedEntityKey;
#if DEBUG
                            // If the constraint is not PK<->PK then we can't validate it here.
                            // This debug code checks that we don't try to validate it.
                            List<string> keyNames = new List<string>(from v in detachedKey.EntityKeyValues select v.Key);
                            foreach (EdmProperty prop in constraint.ToProperties)
                            {
                                Debug.Assert(keyNames.Contains(prop.Name), "Attempt to validate constraint where some FK values are not in the dependent PK");
                            }
#endif
                            // don't need to validate the principal/detached key here because that has already been done during AttachContext
                            if (!VerifyRIConstraintsWithRelatedEntry(constraint, detachedKey.FindValueByName, ownerEntry.EntityKey))
                            {
                                return false;
                            }
                        }
                        else
                        {
                            foreach (IEntityWrapper wrappedRelatedEntity in GetWrappedEntities())
                            {
                                EntityEntry dependent = wrappedRelatedEntity.ObjectStateEntry;
                                if (dependent != null &&
                                    dependent.State != EntityState.Added &&
                                    dependent.State != EntityState.Deleted &&
                                    dependent.State != EntityState.Detached)
                                {
                                    if (!VerifyRIConstraintsWithRelatedEntry(constraint, dependent.GetCurrentEntityValue, ownerEntry.EntityKey))
                                    {
                                        return false;
                                    }
                                }
                            }
                        }
                    }
                    // else keep processing the other constraints
                }
            }
            return true;
        }
 
        private EntityKey ExtractPrincipalKey(IEntityWrapper wrappedRelatedEntity)
        {
            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 = wrappedRelatedEntity.EntityKey;
            if (null != (object)principalKey && !principalKey.IsTemporary)
            {
                // 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(ObjectContext.MetadataWorkspace, principalEntitySet);
            }
            else
            {
                principalKey = _context.ObjectStateManager.CreateEntityKey(principalEntitySet, wrappedRelatedEntity.Entity);
            }
            return principalKey;
        }
 
        internal static bool VerifyRIConstraintsWithRelatedEntry(ReferentialConstraint constraint, Func<string, object> getDependentPropertyValue, 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 = getDependentPropertyValue(toPropertyName);
 
                Debug.Assert(currentValue != null, "currentValue is part of Key on an attached entity, it must not be null");
 
                if (!ByValueEqualityComparer.Default.Equals(currentValue, 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();
            }
            DeferredLoad();
            return GetInternalEnumerable().GetEnumerator();
        }
 
        internal void RemoveAll()
        {
            //copy into list because changing collection member is not allowed during enumeration.
            // If possible avoid copying into list.
            List<IEntityWrapper> deletedEntities = null;
 
            bool fireEvent = false;
            try
            {
                _suppressEvents = true;
                foreach (IEntityWrapper wrappedEntity in GetWrappedEntities())
                {
                    if (null == deletedEntities)
                    {
                        deletedEntities = new List<IEntityWrapper>();
                    }
                    deletedEntities.Add(wrappedEntity);
                }
 
 
                if (fireEvent = (null != deletedEntities) && (deletedEntities.Count > 0))
                {
                    foreach (IEntityWrapper wrappedEntity in deletedEntities)
                    {
                        Remove(wrappedEntity, /*fixup*/true, /*deleteEntity*/false, /*deleteOwner*/true, /*applyReferentialConstraints*/true, /*preserveForeignKey*/false);
                    }
                }
            }
            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<IEntityWrapper> deletedEntities = new List<IEntityWrapper>();
 
            foreach (IEntityWrapper wrappedEntity in GetWrappedEntities())
            {
                deletedEntities.Add(wrappedEntity);
            }
 
            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 separately (when the event is being fired, 
            // we should be in good state: for every entity deleted, related event should have been fired)
            foreach (IEntityWrapper wrappedEntity 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(wrappedEntity))
                {
                    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(wrappedEntity, _wrappedOwner, _relationshipSet, _navigation);
                }
                RelatedEnd relatedEnd = GetOtherEndOfRelationship(wrappedEntity);
                relatedEnd.RemoveFromCache(_wrappedOwner, /* resetIsLoaded */ true, /*preserveForeignKey*/ false);
                relatedEnd.OnAssociationChanged(CollectionChangeAction.Remove, _wrappedOwner.Entity);
            }
 
            // Clear the DetachedEntityKey if this is a foreign key
            if (IsForeignKey)
            {
                EntityReference entityReference = this as EntityReference;
                if (entityReference != null)
                {
                    entityReference.DetachedEntityKey = null;
                }
            }
 
            foreach (IEntityWrapper wrappedEntity in deletedEntities)
            {
                RelatedEnd relatedEnd = GetOtherEndOfRelationship(wrappedEntity);
                this.RemoveFromCache(wrappedEntity, /* resetIsLoaded */ false, /*preserveForeignKey*/ false);
            }
            this.OnAssociationChanged(CollectionChangeAction.Refresh, null);
 
            Debug.Assert(this.IsEmpty(), "Collection or reference should be empty");
        }
 
        #region Add
 
        internal void AddToCache(IEntityWrapper wrappedEntity, bool applyConstraints)
        {
            AddToLocalCache(wrappedEntity, applyConstraints);
            AddToObjectCache(wrappedEntity);
        }
        internal abstract void AddToLocalCache(IEntityWrapper wrappedEntity, bool applyConstraints);
        internal abstract void AddToObjectCache(IEntityWrapper wrappedEntity);
 
        #endregion
 
        #region Remove
 
        internal bool RemoveFromCache(IEntityWrapper wrappedEntity, bool resetIsLoaded, bool preserveForeignKey)
        {
            bool result = RemoveFromLocalCache(wrappedEntity, resetIsLoaded, preserveForeignKey);
            RemoveFromObjectCache(wrappedEntity);
            return result;
        }
        // Remove from the RelatedEnd
        internal abstract bool RemoveFromLocalCache(IEntityWrapper wrappedEntity, bool resetIsLoaded, bool preserveForeignKey);
        // Remove from the underlying POCO navigation property
        internal abstract bool RemoveFromObjectCache(IEntityWrapper wrappedEntity);
 
        #endregion
 
        internal abstract bool VerifyEntityForAdd(IEntityWrapper wrappedEntity, bool relationshipAlreadyExists);
        internal abstract void VerifyType(IEntityWrapper wrappedEntity);
        internal abstract bool CanSetEntityType(IEntityWrapper wrappedEntity);
        internal abstract void Include(bool addRelationshipAsUnchanged, bool doAttach);
        internal abstract void Exclude();
        internal abstract void ClearCollectionOrRef(IEntityWrapper wrappedEntity, RelationshipNavigation navigation, bool doCascadeDelete);
        internal abstract bool ContainsEntity(IEntityWrapper wrappedEntity);
        internal abstract IEnumerable GetInternalEnumerable();
        internal abstract IEnumerable<IEntityWrapper> GetWrappedEntities();
        internal abstract void RetrieveReferentialConstraintProperties(Dictionary<string, KeyValuePair<object, IntBox>> keyValues, HashSet<object> visited);
        internal abstract bool IsEmpty();
        internal abstract void OnRelatedEndClear();
        internal abstract void ClearWrappedValues();
        internal abstract void VerifyMultiplicityConstraintsForAdd(bool applyConstraints);
 
        internal virtual void OnAssociationChanged(CollectionChangeAction collectionChangeAction, object entity)
        {
            Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity.");
            if (!_suppressEvents)
            {
                if (_onAssociationChanged != null)
                {
                    _onAssociationChanged(this, (new CollectionChangeEventArgs(collectionChangeAction, entity)));
                }
            }
        }
 
        private void AddEntityToObjectStateManager(IEntityWrapper wrappedEntity, bool doAttach)
        {
            Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
            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.");
 
            EntitySet es = GetTargetEntitySetFromRelationshipSet();
            if (!doAttach)
            {
                _context.AddSingleObject(es, wrappedEntity, "entity");
            }
            else
            {
                _context.AttachSingleObject(wrappedEntity, 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 = wrappedEntity.EntityKey;
                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 RelationshipEntry AddRelationshipToObjectStateManager(IEntityWrapper wrappedEntity, bool addRelationshipAsUnchanged, bool doAttach)
        {
            Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
            Debug.Assert(!UsingNoTracking, "Should not add Relationship to ObjectStateManager for NoTracking cases.");
            Debug.Assert(!this.IsForeignKey, "for IsForeignKey relationship ObjectStateEntries don't exist");
            Debug.Assert(this._context != null && wrappedEntity.Context != null, "should be called only if both entities are attached");
            Debug.Assert(this._context == wrappedEntity.Context, "both entities should be attached to the same context");
 
            EntityKey ownerKey = _wrappedOwner.EntityKey;
            EntityKey entityKey = wrappedEntity.EntityKey;
            EntityUtil.CheckEntityKeyNull(ownerKey);
            EntityUtil.CheckEntityKeyNull(entityKey);
 
            return this.ObjectContext.ObjectStateManager.AddRelation(
                new RelationshipWrapper((AssociationSet)_relationshipSet,
                       new KeyValuePair<string, EntityKey>(_navigation.From, ownerKey),
                       new KeyValuePair<string, EntityKey>(_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(IEntityWrapper wrappedEntity,
                                                                       bool addRelationshipAsUnchanged, bool doAttach)
        {
            Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
            foreach (RelatedEnd relatedEnd in wrappedEntity.RelationshipManager.Relationships)
            {
                relatedEnd.Include(addRelationshipAsUnchanged, doAttach);
            }
        }
 
        internal static void RemoveEntityFromObjectStateManager(IEntityWrapper wrappedEntity)
        {
            Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
            EntityEntry entry;
 
            if (wrappedEntity.Context != null &&
                wrappedEntity.Context.ObjectStateManager.TransactionManager.IsAttachTracking &&
                wrappedEntity.Context.ObjectStateManager.TransactionManager.PromotedKeyEntries.TryGetValue(wrappedEntity.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(wrappedEntity);
                if (entry != null && entry.State != EntityState.Detached)
                {
                    entry.AcceptChanges();
                }
            }
        }
 
        private static void RemoveRelationshipFromObjectStateManager(IEntityWrapper wrappedEntity, IEntityWrapper wrappedOwner, RelationshipSet relationshipSet, RelationshipNavigation navigation)
        {
            Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
            Debug.Assert(relationshipSet == null || !(relationshipSet.ElementType as AssociationType).IsForeignKey, "for IsForeignKey relationships ObjectStateEntries don't exist");
 
            RelationshipEntry deletedEntry = MarkRelationshipAsDeletedInObjectStateManager(wrappedEntity, wrappedOwner, relationshipSet, navigation);
            if (deletedEntry != null && deletedEntry.State != EntityState.Detached)
            {
                deletedEntry.AcceptChanges();
            }
        }
 
        private void FixupOtherEndOfRelationshipForRemove(IEntityWrapper wrappedEntity, bool preserveForeignKey)
        {
            Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
            RelatedEnd relatedEnd = GetOtherEndOfRelationship(wrappedEntity);
            relatedEnd.Remove(_wrappedOwner, /*fixup*/false, /*deleteEntity*/false, /*deleteOwner*/false, /*applyReferentialConstraints*/false, preserveForeignKey);
            relatedEnd.RemoveFromNavigationProperty(_wrappedOwner);
        }
 
        private static EntityEntry MarkEntityAsDeletedInObjectStateManager(IEntityWrapper wrappedEntity)
        {
            Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
            EntityEntry entry = null;
            if (wrappedEntity.Context != null)
            {
                entry = wrappedEntity.Context.ObjectStateManager.FindEntityEntry(wrappedEntity.Entity);
 
                if (entry != null)
                {
                    entry.Delete(/*doFixup*/false);
                }
            }
            return entry;
        }
 
        private static RelationshipEntry MarkRelationshipAsDeletedInObjectStateManager(IEntityWrapper wrappedEntity, IEntityWrapper wrappedOwner, RelationshipSet relationshipSet, RelationshipNavigation navigation)
        {
            Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
            Debug.Assert(relationshipSet == null || !(relationshipSet.ElementType as AssociationType).IsForeignKey, "for IsForeignKey relationships ObjectStateEntries don't exist");
            RelationshipEntry entry = null;
            if (wrappedOwner.Context != null && wrappedEntity.Context != null && relationshipSet != null)
            {
                EntityKey ownerKey = wrappedOwner.EntityKey;
                EntityKey entityKey = wrappedEntity.EntityKey;
 
                entry = wrappedEntity.Context.ObjectStateManager.DeleteRelationship(relationshipSet,
                                      new KeyValuePair<string, EntityKey>(navigation.From, ownerKey),
                                      new KeyValuePair<string, EntityKey>(navigation.To, entityKey));
            }
            return entry;
        }
 
        private static void DetachRelationshipFromObjectStateManager(IEntityWrapper wrappedEntity, IEntityWrapper wrappedOwner, RelationshipSet relationshipSet, RelationshipNavigation navigation)
        {
            Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
            if (wrappedOwner.Context != null && wrappedEntity.Context != null && relationshipSet != null)
            {
                EntityKey ownerKey = wrappedOwner.EntityKey;
                EntityKey entityKey = wrappedEntity.EntityKey;
                RelationshipEntry entry = wrappedEntity.Context.ObjectStateManager.FindRelationship(relationshipSet,
                                                                          new KeyValuePair<string, EntityKey>(navigation.From, ownerKey),
                                                                          new KeyValuePair<string, EntityKey>(navigation.To, entityKey));
                if (entry != null)
                {
                    entry.DetachRelationshipEntry();
                }
            }
        }
 
        private static void RemoveEntityFromRelatedEnds(IEntityWrapper wrappedEntity1, IEntityWrapper wrappedEntity2, RelationshipNavigation navigation)
        {
            Debug.Assert(wrappedEntity1 != null, "IEntityWrapper instance is null.");
            Debug.Assert(wrappedEntity2 != null, "IEntityWrapper instance is null.");
            foreach (RelatedEnd relatedEnd in wrappedEntity1.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(wrappedEntity2, 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)
        {
            if (!_wrappedOwner.InitializingProxyRelatedEnds)
            {
                EntityKey ownerKey = _wrappedOwner.EntityKey;
                EntityUtil.CheckEntityKeyNull(ownerKey);
                EntitySet entitySet = ownerKey.GetEntitySet(context.MetadataWorkspace);
 
                AttachContext(context, entitySet, mergeOption);
            }
        }
 
 
        /// <summary>
        /// Set the context and load options so that Query can be constructed on demand.
        /// </summary>
        internal void AttachContext(ObjectContext context, EntitySet entitySet, MergeOption mergeOption)
        {
            EntityUtil.CheckArgumentNull(context, "context");
            EntityUtil.CheckArgumentMergeOption(mergeOption);
            EntityUtil.CheckArgumentNull(entitySet, "entitySet");
 
            _wrappedOwner.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);
 
                EdmType relationshipType;
                RelationshipSet relationshipSet;
                FindRelationshipSet(_context, entitySet, out relationshipType, out relationshipSet);
 
                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)
                    {
                        Debug.Assert(!foundFromRelationEnd, "More than one related end was found with the same role name.");
 
                        foundFromRelationEnd = true;
                        this._fromEndProperty = relationEnd;
                    }
                    if (relationEnd.Name == this._navigation.To)
                    {
                        Debug.Assert(!foundToRelationEnd, "More than one related end was found with the same role name.");
 
                        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(ObjectContext.MetadataWorkspace, 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();
                }
            }
        }
 
        internal void FindRelationshipSet(ObjectContext context, EntitySet entitySet, out EdmType relationshipType, out RelationshipSet relationshipSet)
        {
            // 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
            relationshipType = context.MetadataWorkspace.GetItem<EdmType>(_navigation.RelationshipName, DataSpace.CSpace);
            if (relationshipType == null)
            {
                throw EntityUtil.NoRelationshipSetMatched(_navigation.RelationshipName);
            }
 
            // find the RelationshipSet
            foreach (EntitySetBase entitySetBase in entitySet.EntityContainer.BaseEntitySets)
            {
                if ((EdmType)entitySetBase.ElementType == relationshipType)
                {
                    if (((AssociationSet)entitySetBase).AssociationSetEnds[_navigation.From].EntitySet == entitySet)
                    {
                        relationshipSet = (RelationshipSet)entitySetBase;
                        return;
                    }
                }
            }
            relationshipSet = null;
        }
 
        /// <summary>
        /// Clear the source and context.
        /// </summary>        
        internal void DetachContext()
        {
            if (this._context != null &&
                this.ObjectContext.ObjectStateManager.TransactionManager.IsAttachTracking &&
                this.ObjectContext.ObjectStateManager.TransactionManager.OriginalMergeOption == MergeOption.NoTracking)
            {
                this._usingNoTracking = true;
                return;
            }
 
            this._sourceQuery = null;
            this._context = null;
            this._relationshipSet = null;
            this._fromEndProperty = null;
            this._toEndProperty = null;
            this._relationMetadata = null;
 
            // Detached entity should have IsLoaded property set to false
            this._isLoaded = false;
        }
 
        /// <summary>
        ///  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.
        /// </summary>
        /// <param name="query">query of U</param>
        /// <returns></returns>
        internal static IEnumerable<U> GetResults<U>(ObjectQuery<U> query)
        {
            return query.Execute(query.MergeOption);
        }
 
        internal RelatedEnd GetOtherEndOfRelationship(IEntityWrapper wrappedEntity)
        {
            Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
            EnsureRelationshipNavigationAccessorsInitialized();
            return (RelatedEnd)wrappedEntity.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 (_wrappedOwner.Entity == 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(IEntityWrapper wrappedOwner, RelationshipNavigation navigation, IRelationshipFixer relationshipFixer)
        {
            SetWrappedOwner(wrappedOwner);
            _navigation = navigation;
            _relationshipFixer = relationshipFixer;
        }
 
        internal void SetWrappedOwner(IEntityWrapper wrappedOwner)
        {
            _wrappedOwner = wrappedOwner != null ? wrappedOwner : EntityWrapperFactory.NullWrapper;
#pragma warning disable 612 // Disable "obsolete" warning for the _owner field. Used for backwards compatibility.
            _owner = wrappedOwner.Entity as IEntityWithRelationships;
#pragma warning restore 612
        }
 
        internal static bool IsValidEntityKeyType(EntityKey entityKey)
        {
            return !(entityKey.IsTemporary ||
                     Object.ReferenceEquals(EntityKey.EntityNotValidKey, entityKey) ||
                     Object.ReferenceEquals(EntityKey.NoEntitySetKey, entityKey));
        }
 
        // This method is required to maintain compatibility with the v1 binary serialization format. 
        // In particular, it recreates a entity wrapper from the serialized owner.
        // Note that this is only expected to work for non-POCO entities, since serialization of POCO
        // entities will not result in serialization of the RelationshipManager or its related objects.
        [OnDeserialized()]
        [Browsable(false)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void OnDeserialized(StreamingContext context)
        {
#pragma warning disable 612 // Disable "obsolete" warning for the _owner field. Used for backwards compatibility.
            _wrappedOwner = EntityWrapperFactory.WrapEntityUsingContext(_owner, ObjectContext);
#pragma warning restore 612
        }
 
        [NonSerialized]
        private NavigationProperty navigationPropertyCache = null;
 
        internal NavigationProperty NavigationProperty
        {
            get
            {
                if (this.navigationPropertyCache == null && _wrappedOwner.Context != null && this.TargetAccessor.HasProperty)
                {
                    string navigationPropertyName = this.TargetAccessor.PropertyName;
 
                    EntityType entityType = _wrappedOwner.Context.MetadataWorkspace.GetItem<EntityType>(_wrappedOwner.IdentityType.FullName, DataSpace.OSpace);
                    NavigationProperty member;
                    if (!entityType.NavigationProperties.TryGetValue(navigationPropertyName, false, out member))
                    {
                        throw new InvalidOperationException(System.Data.Entity.Strings.RelationshipManager_NavigationPropertyNotFound(navigationPropertyName));
                    }
                    // Avoid metadata lookups by caching the navigation property locally
                    this.navigationPropertyCache = member;
                }
                return this.navigationPropertyCache;
            }
        }
 
        #region POCO Navigation Property Accessors
 
        internal NavigationPropertyAccessor TargetAccessor
        {
            get
            {
                if (_wrappedOwner.Entity != null)
                {
                    EnsureRelationshipNavigationAccessorsInitialized();
                    return RelationshipNavigation.ToPropertyAccessor;
                }
                else
                {
                    // Disconnected RelatedEnds have no POCO navigation properties
                    return NavigationPropertyAccessor.NoNavigationProperty;
                }
            }
        }
 
        // If the RelationshipNavigation has not been fully initialized, it means this RelatedEnd was created without metadata
        // This can occur in serialization scenarios
        // Try to look up the metadata in all metadata repositories that are available and populate it
        // This must be called before accessing any of the Accessor properties on the RelationshipNavigation
        private void EnsureRelationshipNavigationAccessorsInitialized()
        {
            Debug.Assert(_navigation != null, "Null RelationshipNavigation");
            Debug.Assert(_wrappedOwner.Entity != null, "Must be connected to lookup metadata");
            if (!RelationshipNavigation.IsInitialized)
            {
                NavigationPropertyAccessor sourceAccessor = null;
                NavigationPropertyAccessor targetAccessor = null;
 
                AssociationType associationType = this.RelationMetadata as AssociationType;
                string relationshipName = _navigation.RelationshipName;
                string sourceRoleName = _navigation.From;
                string targetRoleName = _navigation.To;
                if (associationType != null ||
                    RelationshipManager.TryGetRelationshipType(WrappedOwner, WrappedOwner.IdentityType, relationshipName, out associationType) ||
                    EntityProxyFactory.TryGetAssociationTypeFromProxyInfo(WrappedOwner, relationshipName, targetRoleName, out associationType))
                {
                    AssociationEndMember sourceEnd;
                    if (associationType.AssociationEndMembers.TryGetValue(sourceRoleName, false, out sourceEnd))
                    {
                        EntityType sourceEntityType = MetadataHelper.GetEntityTypeForEnd(sourceEnd);
                        targetAccessor = MetadataHelper.GetNavigationPropertyAccessor(sourceEntityType, relationshipName, sourceRoleName, targetRoleName);
                    }
 
                    AssociationEndMember targetEnd;
                    if (associationType.AssociationEndMembers.TryGetValue(targetRoleName, false, out targetEnd))
                    {
                        EntityType targetEntityType = MetadataHelper.GetEntityTypeForEnd(targetEnd);
                        sourceAccessor = MetadataHelper.GetNavigationPropertyAccessor(targetEntityType, relationshipName, targetRoleName, sourceRoleName);
                    }
                }
 
                if (sourceAccessor == null || targetAccessor == null)
                {
                    throw RelationshipManager.UnableToGetMetadata(WrappedOwner, relationshipName);
                }
 
                RelationshipNavigation.InitializeAccessors(sourceAccessor, targetAccessor);
            }
        }
 
        #endregion
    }
}