entityreference_tresulttype.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

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

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner       [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 

using System.Collections; 
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Common; 
using System.Data.Common.CommandTrees;
using System.Data.Objects.Internal; 
using System.Data.Metadata.Edm; 
using System.Diagnostics;
using System.Runtime.Serialization; 
using System.Linq;

namespace System.Data.Objects.DataClasses
{ 
    /// 
    /// Models a relationship end with multiplicity 1. 
    ///  
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
    [DataContract] 
    [Serializable]
    public sealed class EntityReference : EntityReference
        where TEntity : class
    { 
        // ------
        // Fields 
        // ------ 

        // 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.
        // Note that this field should no longer be used directly.  Instead, use the _wrappedCachedValue 
        // field.  This field is retained only for compatability with the serialization format introduced in v1.
        private TEntity _cachedValue; 
 
        [NonSerialized]
        private IEntityWrapper _wrappedCachedValue; 

        // ------------
        // Constructors
        // ------------ 

        ///  
        /// The default constructor is required for some serialization scenarios. It should not be used to 
        /// create new EntityReferences. Use the GetRelatedReference or GetRelatedEnd methods on the RelationshipManager
        /// class instead. 
        /// 
        public EntityReference()
        {
            _wrappedCachedValue = EntityWrapperFactory.NullWrapper; 
        }
 
        internal EntityReference(IEntityWrapper wrappedOwner, RelationshipNavigation navigation, IRelationshipFixer relationshipFixer) 
            : base(wrappedOwner, navigation, relationshipFixer)
        { 
            _wrappedCachedValue = EntityWrapperFactory.NullWrapper;
        }

        // ---------- 
        // Properties
        // ---------- 
 
        /// 
        /// Stub only please replace with actual implementation 
        /// 
        [System.Xml.Serialization.SoapIgnore]
        [System.Xml.Serialization.XmlIgnore]
        public TEntity Value 
        {
            get 
            { 
                DeferredLoad();
                return (TEntity)ReferenceValue.Entity; 
            }
            set
            {
                ReferenceValue = EntityWrapperFactory.WrapEntityUsingContext(value, ObjectContext); 
            }
        } 
 
        internal override IEntityWrapper CachedValue
        { 
            get { return _wrappedCachedValue; }
        }

        internal override IEntityWrapper ReferenceValue 
        {
            get 
            { 
                CheckOwnerNull();
                return _wrappedCachedValue; 
            }
            set
            {
                CheckOwnerNull(); 
                //setting to same value is a no-op (SQLBU DT # 446320)
                //setting to null is a special case because then we will also clear out any Added/Unchanged relationships with key entries, so we can't no-op if Value is null 
                if (value.Entity != null && value.Entity == _wrappedCachedValue.Entity) 
                {
                    return; 
                }

                if (null != value.Entity)
                { 
                    // Note that this is only done for the case where we are not setting the ref to null because
                    // clearing a ref is okay--it will cause the dependent to become deleted/detached. 
                    ValidateOwnerWithRIConstraints(value, value == EntityWrapperFactory.NullWrapper ? null : value.EntityKey, checkBothEnds: true); 
                    ObjectContext context = ObjectContext ?? value.Context;
                    if (context != null) 
                    {
                        context.ObjectStateManager.TransactionManager.EntityBeingReparented = GetDependentEndOfReferentialConstraint(value.Entity);
                    }
                    try 
                    {
                        Add(value, /*applyConstraints*/false); 
                    } 
                    finally
                    { 
                        if (context != null)
                        {
                            context.ObjectStateManager.TransactionManager.EntityBeingReparented = null;
                        } 
                    }
                } 
                else 
                {
                    if (UsingNoTracking) 
                    {
                        if (_wrappedCachedValue.Entity != null)
                        {
                            // The other end of relationship can be the EntityReference or EntityCollection 
                            // If the other end is EntityReference, its IsLoaded property should be set to FALSE
                            RelatedEnd relatedEnd = GetOtherEndOfRelationship(_wrappedCachedValue); 
                            relatedEnd.OnRelatedEndClear(); 
                        }
 
                        _isLoaded = false;
                    }
                    ClearCollectionOrRef(null, null, false);
                } 
            }
        } 
 
        // -------
        // Methods 
        // -------

        /// 
        /// Loads the related entity or entities into the local related end using the supplied MergeOption. 
        /// 
        public override void Load(MergeOption mergeOption) 
        { 
            CheckOwnerNull();
 
            // Validate that the Load is possible
            bool hasResults;
            ObjectQuery sourceQuery = ValidateLoad(mergeOption, "EntityReference", out hasResults);
 
            _suppressEvents = true; // we do not want any event during the bulk operation
            try 
            { 
                List refreshedValue = null;
                if (hasResults) 
                {
                    // Only issue a query if we know it can produce results (in the case of FK, there may not be any
                    // results).
                    refreshedValue = new List(GetResults(sourceQuery)); 
                }
                if (null == refreshedValue || refreshedValue.Count == 0) 
                { 
                    if (!((AssociationType)base.RelationMetadata).IsForeignKey && ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One)
                    { 
                        //query returned zero related end; one related end was expected.
                        throw EntityUtil.LessThanExpectedRelatedEntitiesFound();
                    }
                    else if (mergeOption == MergeOption.OverwriteChanges || mergeOption == MergeOption.PreserveChanges) 
                    {
                        // This entity is not related to anything in this AssociationSet and Role on the server. 
                        // If there is an existing _cachedValue, we may need to clear it out, based on the MergeOption 
                        EntityKey sourceKey = WrappedOwner.EntityKey;
                        EntityUtil.CheckEntityKeyNull(sourceKey); 
                        ObjectStateManager.RemoveRelationships(ObjectContext, mergeOption, (AssociationSet)RelationshipSet, sourceKey, (AssociationEndMember)FromEndProperty);
                    }
                    // else this is NoTracking or AppendOnly, and no entity was retrieved by the Load, so there's nothing extra to do
 
                    // Since we have no value and are not doing a merge, the last step is to set IsLoaded to true
                    _isLoaded = true; 
                } 
                else if (refreshedValue.Count == 1)
                { 
                    Merge(refreshedValue, mergeOption, true /*setIsLoaded*/);
                }
                else
                { 
                    // More than 1 result, which is non-recoverable data inconsistency
                    throw EntityUtil.MoreThanExpectedRelatedEntitiesFound(); 
                } 
            }
            finally 
            {
                _suppressEvents = false;
            }
            // fire the AssociationChange with Refresh 
            OnAssociationChanged(CollectionChangeAction.Refresh, null);
        } 
 
        /// 
        /// This operation is not allowed if the owner is null 
        /// 
        /// 
        internal override IEnumerable GetInternalEnumerable()
        { 
            if (ReferenceValue.Entity != null)
            { 
                yield return (TEntity)ReferenceValue.Entity; 
            }
        } 

        internal override IEnumerable GetWrappedEntities()
        {
            // 
            return _wrappedCachedValue.Entity == null ? new IEntityWrapper[0] : new IEntityWrapper[] { _wrappedCachedValue };
        } 
 
        /// 
        /// Attaches an entity to the EntityReference. The given 
        /// entity is not assumed to be the complete set of related entities.
        ///
        /// Owner and all entities passed in must be in Unchanged or Modified state.
        /// Deleted elements are allowed only when the state manager is already tracking the relationship 
        /// instance.
        ///  
        /// The entity to attach to the EntityCollection 
        /// Thrown when  is null.
        /// Thrown when the entity cannot be related via the current relationship end. 
        public void Attach(TEntity entity)
        {
            CheckOwnerNull();
            EntityUtil.CheckArgumentNull(entity, "entity"); 
            Attach(new IEntityWrapper[] { EntityWrapperFactory.WrapEntityUsingContext(entity, ObjectContext) }, false);
        } 
 
        internal override void Include(bool addRelationshipAsUnchanged, bool doAttach)
        { 
            Debug.Assert(this.ObjectContext != null, "Should not be trying to add entities to state manager if context is null");

            // If we have an actual value or a key for this reference, add it to the context
            if (null != _wrappedCachedValue.Entity) 
            {
                // Sometimes with mixed POCO and IPOCO, you can get different instances of IEntityWrappers stored in the IPOCO related ends 
                // These should be replaced by the IEntityWrapper that is stored in the context 
                IEntityWrapper identityWrapper = EntityWrapperFactory.WrapEntityUsingContext(_wrappedCachedValue.Entity, WrappedOwner.Context);
                if (identityWrapper != _wrappedCachedValue) 
                {
                    _wrappedCachedValue = identityWrapper;
                }
                IncludeEntity(_wrappedCachedValue, addRelationshipAsUnchanged, doAttach); 
            }
            else if (DetachedEntityKey != null) 
            { 
                IncludeEntityKey(doAttach);
            } 
            // else there is nothing to add for this relationship
        }

        private void IncludeEntityKey(bool doAttach) 
        {
            ObjectStateManager manager = this.ObjectContext.ObjectStateManager; 
 
            bool addNewRelationship = false;
            bool addKeyEntry = false; 
            EntityEntry existingEntry = manager.FindEntityEntry(DetachedEntityKey);
            if (existingEntry == null)
            {
                // add new key entry and create a relationship with it 
                addKeyEntry = true;
                addNewRelationship = true; 
            } 
            else
            { 
                if (existingEntry.IsKeyEntry)
                {
                    // We have an existing key entry, so just need to add a relationship with it
 
                    // We know the target end of this relationship is 1..1 or 0..1 since it is a reference, so if the source end is also not Many, we have a 1-to-1
                    if (FromEndProperty.RelationshipMultiplicity != RelationshipMultiplicity.Many) 
                    { 
                        // before we add a new relationship to this key entry, make sure it's not already related to something else
                        // We have to explicitly do this here because there are no other checks to make sure a key entry in a 1-to-1 doesn't end up in two of the same relationship 
                        foreach (RelationshipEntry relationshipEntry in this.ObjectContext.ObjectStateManager.FindRelationshipsByKey(DetachedEntityKey))
                        {
                            // only care about relationships in the same AssociationSet and where the key is playing the same role that it plays in this EntityReference
                            if (relationshipEntry.IsSameAssociationSetAndRole((AssociationSet)RelationshipSet, (AssociationEndMember)ToEndMember, DetachedEntityKey) && 
                                relationshipEntry.State != EntityState.Deleted)
                            { 
                                throw EntityUtil.EntityConflictsWithKeyEntry(); 
                            }
                        } 
                    }

                    addNewRelationship = true;
                } 
                else
                { 
                    IEntityWrapper wrappedTarget = existingEntry.WrappedEntity; 

                    // Verify that the target entity is in a valid state for adding a relationship 
                    if (existingEntry.State == EntityState.Deleted)
                    {
                        throw EntityUtil.UnableToAddRelationshipWithDeletedEntity();
                    } 

                    // We know the target end of this relationship is 1..1 or 0..1 since it is a reference, so if the source end is also not Many, we have a 1-to-1 
                    RelatedEnd relatedEnd = wrappedTarget.RelationshipManager.GetRelatedEndInternal(RelationshipName, RelationshipNavigation.From); 
                    if (FromEndProperty.RelationshipMultiplicity != RelationshipMultiplicity.Many && !relatedEnd.IsEmpty())
                    { 
                        // Make sure the target entity is not already related to something else.
                        // devnote: The call to Add below does *not* do this check for the fixup case, so if it's not done here, no failure will occur
                        //          and existing relationships may be deleted unexpectedly. RelatedEnd.Include should not remove existing relationships, only add new ones.
                        throw EntityUtil.EntityConflictsWithKeyEntry(); 
                    }
 
                    // We have an existing entity with the same key, just hook up the related ends 
                    this.Add(wrappedTarget,
                        applyConstraints: true, 
                        addRelationshipAsUnchanged: doAttach,
                        relationshipAlreadyExists: false,
                        allowModifyingOtherEndOfRelationship: true,
                        forceForeignKeyChanges: true); 

                    // add to the list of promoted key references so we can cleanup if a failure occurs later 
                    manager.TransactionManager.PopulatedEntityReferences.Add(this); 
                }
            } 

            // For FKs, don't create a key entry and don't create a relationship
            if (addNewRelationship && !IsForeignKey)
            { 
                // devnote: If we add any validation here, it needs to go here before adding the key entry,
                //          otherwise we have to clean up that entry if the validation fails 
 
                if (addKeyEntry)
                { 
                    EntitySet targetEntitySet = DetachedEntityKey.GetEntitySet(this.ObjectContext.MetadataWorkspace);
                    manager.AddKeyEntry(DetachedEntityKey, targetEntitySet);
                }
 
                EntityKey ownerKey = WrappedOwner.EntityKey;
                EntityUtil.CheckEntityKeyNull(ownerKey); 
                RelationshipWrapper wrapper = new RelationshipWrapper((AssociationSet)RelationshipSet, 
                    RelationshipNavigation.From, ownerKey, RelationshipNavigation.To, DetachedEntityKey);
                manager.AddNewRelation(wrapper, doAttach ? EntityState.Unchanged : EntityState.Added); 
            }
        }

        internal override void Exclude() 
        {
            Debug.Assert(this.ObjectContext != null, "Should not be trying to remove entities from state manager if context is null"); 
 
            if (null != _wrappedCachedValue.Entity)
            { 
                // It is possible that _cachedValue was originally null in this graph, but was only set
                // while the graph was being added, if the DetachedEntityKey matched its key. In that case,
                // we only want to clear _cachedValue and delete the relationship entry, but not remove the entity
                // itself from the context. 
                TransactionManager transManager = ObjectContext.ObjectStateManager.TransactionManager;
                bool doFullRemove = transManager.PopulatedEntityReferences.Contains(this); 
                bool doRelatedEndRemove = transManager.AlignedEntityReferences.Contains(this); 
                // For POCO, if the entity is undergoing snapshot for the first time, then in this step we actually
                // need to really exclude it rather than just disconnecting it.  If we don't, then it has the potential 
                // to remain in the context at the end of the rollback process.
                if ((transManager.ProcessedEntities == null || !transManager.ProcessedEntities.Contains(_wrappedCachedValue)) &&
                    (doFullRemove || doRelatedEndRemove))
                { 
                    // Retrieve the relationship entry before _cachedValue is set to null during Remove
                    RelationshipEntry relationshipEntry = IsForeignKey ? null : FindRelationshipEntryInObjectStateManager(_wrappedCachedValue); 
                    Debug.Assert(IsForeignKey || relationshipEntry != null, "Should have been able to find a valid relationship since _cachedValue is non-null"); 

                    // Remove the related ends and mark the relationship as deleted, but don't propagate the changes to the target entity itself 
                    Remove(_wrappedCachedValue,
                            doFixup: doFullRemove,
                            deleteEntity: false,
                            deleteOwner: false, 
                            applyReferentialConstraints: false,
                            preserveForeignKey: true); 
 
                    // The relationship will now either be detached (if it was previously in the Added state), or Deleted (if it was previously Unchanged)
                    // If it's Deleted, we need to AcceptChanges to get rid of it completely 
                    if (relationshipEntry != null && relationshipEntry.State != EntityState.Detached)
                    {
                        relationshipEntry.AcceptChanges();
                    } 

                    // Since this has been processed, remove it from the list 
                    if (doFullRemove) 
                    {
                        transManager.PopulatedEntityReferences.Remove(this); 
                    }
                    else
                    {
                        transManager.AlignedEntityReferences.Remove(this); 
                    }
                } 
                else 
                {
                    ExcludeEntity(_wrappedCachedValue); 
                }
            }
            else if (DetachedEntityKey != null)
            { 
                // there may still be relationship entries with stubs that need to be removed
                // this works whether we just added the key entry along with the relationship or if it was already existing 
                ExcludeEntityKey(); 
            }
            // else there is nothing to remove for this relationship 
        }

        private void ExcludeEntityKey()
        { 
            EntityKey ownerKey = WrappedOwner.EntityKey;
 
            RelationshipEntry relationshipEntry = this.ObjectContext.ObjectStateManager.FindRelationship(RelationshipSet, 
                new KeyValuePair(RelationshipNavigation.From, ownerKey),
                new KeyValuePair(RelationshipNavigation.To, DetachedEntityKey)); 

            // we may have failed in adding the graph before we actually added this relationship, so make sure we actually found one
            if (relationshipEntry != null)
            { 
                relationshipEntry.Delete(/*doFixup*/ false);
                // If entry was Added before, it is now Detached, otherwise AcceptChanges to detach it 
                if (relationshipEntry.State != EntityState.Detached) 
                {
                    relationshipEntry.AcceptChanges(); 
                }
            }
        }
 
        internal override void ClearCollectionOrRef(IEntityWrapper wrappedEntity, RelationshipNavigation navigation, bool doCascadeDelete)
        { 
            if (wrappedEntity == null) 
            {
                wrappedEntity = EntityWrapperFactory.NullWrapper; 
            }
            if (null != _wrappedCachedValue.Entity)
            {
                // Following condition checks if we have already visited this graph node. If its true then 
                // we should not do fixup because that would cause circular loop
                if ((wrappedEntity.Entity == _wrappedCachedValue.Entity) && (navigation.Equals(this.RelationshipNavigation))) 
                { 
                    Remove(_wrappedCachedValue, /*fixup*/false, /*deleteEntity*/false, /*deleteOwner*/false, /*applyReferentialConstraints*/false, /*preserveForeignKey*/false);
                } 
                else
                {
                    Remove(_wrappedCachedValue, /*fixup*/true, doCascadeDelete, /*deleteOwner*/false, /*applyReferentialConstraints*/true, /*preserveForeignKey*/false);
                } 
            }
            else 
            { 
                // this entity reference could be replacing a relationship that points to a key entry
                // we need to search relationships on the Owner entity to see if this is true, and if so remove the relationship entry 
                if (WrappedOwner.Entity != null && WrappedOwner.Context != null && !UsingNoTracking)
                {
                    EntityEntry ownerEntry = WrappedOwner.Context.ObjectStateManager.GetEntityEntry(WrappedOwner.Entity);
                    ownerEntry.DeleteRelationshipsThatReferenceKeys(this.RelationshipSet, this.ToEndMember); 
                }
            } 
 
            // If we have an Owner, clear the DetachedEntityKey.
            // If we do not have an owner, retain the key so that we can resolve the difference when the entity is attached to a context 
            if (this.WrappedOwner.Entity != null)
            {
                // Clear the detachedEntityKey as well. In cases where we have to fix up the detachedEntityKey, we will not always be able to detect
                // if we have *only* a Deleted relationship for a given entity/relationship/role, so clearing this here will ensure that 
                // even if no other relationships are added, the key value will still be correct.
                ((EntityReference)this).DetachedEntityKey = null; 
            } 
        }
 
        internal override void ClearWrappedValues()
        {
            this._cachedValue = null;
            this._wrappedCachedValue = NullEntityWrapper.NullWrapper; 
        }
 
        ///  
        ///
        ///  
        /// 
        /// 
        /// True if the verify succeeded, False if the Add should no-op
        internal override bool VerifyEntityForAdd(IEntityWrapper wrappedEntity, bool relationshipAlreadyExists) 
        {
            if (!relationshipAlreadyExists && this.ContainsEntity(wrappedEntity)) 
            { 
                return false;
            } 

            this.VerifyType(wrappedEntity);

            return true; 
        }
 
        internal override bool CanSetEntityType(IEntityWrapper wrappedEntity) 
        {
            return wrappedEntity.Entity is TEntity; 
        }

        internal override void VerifyType(IEntityWrapper wrappedEntity)
        { 
            if (!CanSetEntityType(wrappedEntity))
            { 
                throw EntityUtil.InvalidContainedTypeReference(wrappedEntity.Entity.GetType().FullName, typeof(TEntity).FullName); 
            }
        } 

        /// 
        /// Disconnected adds are not supported for an EntityReference so we should report this as an error.
        ///  
        /// The entity to add to the related end in a disconnected state.
        internal override void DisconnectedAdd(IEntityWrapper wrappedEntity) 
        { 
            CheckOwnerNull();
        } 

        /// 
        /// Disconnected removes are not supported for an EntityReference so we should report this as an error.
        ///  
        /// The entity to remove from the related end in a disconnected state.
        internal override bool DisconnectedRemove(IEntityWrapper wrappedEntity) 
        { 
            CheckOwnerNull();
            return false; 
        }

        /// 
        /// Remove from the RelatedEnd 
        /// 
        ///  
        ///  
        /// 
        internal override bool RemoveFromLocalCache(IEntityWrapper wrappedEntity, bool resetIsLoaded, bool preserveForeignKey) 
        {
            Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
            Debug.Assert(null == _wrappedCachedValue.Entity || wrappedEntity.Entity == _wrappedCachedValue.Entity, "The specified object is not a part of this relationship.");
 
            _wrappedCachedValue = EntityWrapperFactory.NullWrapper;
            _cachedValue = null; 
 
            if (resetIsLoaded)
            { 
                _isLoaded = false;
            }

            // This code sets nullable FK properties on a dependent end to null when a relationship has been nulled. 
            if (ObjectContext != null && IsForeignKey && !preserveForeignKey)
            { 
                NullAllForeignKeys(); 
            }
            return true; 
        }

        /// 
        /// Remove from the POCO collection 
        /// 
        ///  
        ///  
        internal override bool RemoveFromObjectCache(IEntityWrapper wrappedEntity)
        { 
            Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");

            // For POCO entities - clear the CLR reference
            if (this.TargetAccessor.HasProperty) 
            {
                this.WrappedOwner.RemoveNavigationPropertyValue(this, (TEntity)wrappedEntity.Entity); 
            } 

            return true; 
        }

        // Method used to retrieve properties from principal entities.
        // NOTE: 'properties' list is modified in this method and may already contains some properties. 
        internal override void RetrieveReferentialConstraintProperties(Dictionary> properties, HashSet visited)
        { 
            Debug.Assert(properties != null); 

            if (this._wrappedCachedValue.Entity != null) 
            {
                // Dictionary< propertyName, >
                Dictionary> retrievedProperties;
 
                // PERFORMANCE: ReferentialConstraints collection in typical scenario is very small (1-3 elements)
                foreach (ReferentialConstraint constraint in ((AssociationType)this.RelationMetadata).ReferentialConstraints) 
                { 
                    if (constraint.ToRole == FromEndProperty)
                    { 
                        // Detect circular references
                        if (visited.Contains(_wrappedCachedValue))
                        {
                            throw EntityUtil.CircularRelationshipsWithReferentialConstraints(); 
                        }
                        visited.Add(_wrappedCachedValue); 
 
                        _wrappedCachedValue.RelationshipManager.RetrieveReferentialConstraintProperties(out retrievedProperties, visited, includeOwnValues: true);
 
                        Debug.Assert(retrievedProperties != null);
                        Debug.Assert(constraint.FromProperties.Count == constraint.ToProperties.Count, "Referential constraints From/To properties list have different size");

                        // Following loop rewrites properties from "retrievedProperties" into "properties". 
                        // At the same time, property's name is translated from name from principal end into name from dependent end:
                        // Example: Client - Order 
                        //          Client is principal end, Order is dependent end, Client.C_ID == Order.Client_ID 
                        // Input : retrievedProperties = { "C_ID" = 123 }
                        // Output: properties = { "Client_ID" = 123 } 

                        // NOTE order of properties in collections constraint.From/ToProperties is important
                        for (int i = 0; i < constraint.FromProperties.Count; ++i)
                        { 
                            EntityEntry.AddOrIncreaseCounter(
                                    properties, 
                                    constraint.ToProperties[i].Name, 
                                    retrievedProperties[constraint.FromProperties[i].Name].Key);
                        } 
                    }
                }
            }
        } 

        internal override bool IsEmpty() 
        { 
            return _wrappedCachedValue.Entity == null;
        } 

        internal override void VerifyMultiplicityConstraintsForAdd(bool applyConstraints)
        {
            if (applyConstraints && !this.IsEmpty()) 
            {
                throw EntityUtil.CannotAddMoreThanOneEntityToEntityReference(this.RelationshipNavigation.To, this.RelationshipNavigation.RelationshipName); 
            } 
        }
 
        // Update IsLoaded flag if necessary
        // This method is called when Clear() was called on the other end of relationship (if the other end is EntityCollection)
        // or when Value property of the other end was set to null (if the other end is EntityReference).
        // This method is used only when NoTracking option was used. 
        internal override void OnRelatedEndClear()
        { 
            // If other end of relationship was loaded, it mean that this end was also cleared. 
            _isLoaded = false;
        } 

        internal override bool ContainsEntity(IEntityWrapper wrappedEntity)
        {
            // Using operator 'as' instead of () allows calling ContainsEntity 
            // with entity of different type than TEntity.
            return null != _wrappedCachedValue.Entity && _wrappedCachedValue.Entity == wrappedEntity.Entity; 
        } 

        // Identical code is in EntityCollection, but this can't be moved to the base class because it relies on the 
        // knowledge of the generic type, and the base class isn't generic
        public ObjectQuery CreateSourceQuery()
        {
            CheckOwnerNull(); 
            bool hasResults;
            return CreateSourceQuery(DefaultMergeOption, out hasResults); 
        } 

        internal override IEnumerable CreateSourceQueryInternal() 
        {
            return CreateSourceQuery();
        }
        //End identical code 

        ///  
        /// Take any values in the incoming RelatedEnd and sets them onto the values 
        /// that currently exist in this RelatedEnd
        ///  
        /// 
        internal void InitializeWithValue(RelatedEnd relatedEnd)
        {
            Debug.Assert(this._wrappedCachedValue.Entity == null, "The EntityReference already has a value."); 
            EntityReference reference = relatedEnd as EntityReference;
            if (reference != null && reference._wrappedCachedValue.Entity != null) 
            { 
                _wrappedCachedValue = reference._wrappedCachedValue;
                _cachedValue = (TEntity)_wrappedCachedValue.Entity; 
            }
        }

        internal override bool CheckIfNavigationPropertyContainsEntity(IEntityWrapper wrapper) 
        {
            Debug.Assert(this.RelationshipNavigation != null, "null RelationshipNavigation"); 
 
            // If the navigation property doesn't exist (e.g. uni-directional prop), then it can't contain the entity.
            if (!TargetAccessor.HasProperty) 
            {
                return false;
            }
 
            object value = this.WrappedOwner.GetNavigationPropertyValue(this);
 
            return Object.Equals(value, wrapper.Entity); 
        }
 
        internal override void VerifyNavigationPropertyForAdd(IEntityWrapper wrapper)
        {
            if (this.TargetAccessor.HasProperty)
            { 
                object value = WrappedOwner.GetNavigationPropertyValue(this);
                if (!Object.ReferenceEquals(null, value) && !Object.Equals(value, wrapper.Entity)) 
                { 
                    throw EntityUtil.CannotAddMoreThanOneEntityToEntityReference(
                        this.RelationshipNavigation.To, this.RelationshipNavigation.RelationshipName); 
                }
            }
        }
 
        // This method is required to maintain compatability with the v1 binary serialization format.
        // In particular, it recreates a entity wrapper from the serialized cached value. 
        // 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 OnRefDeserialized(StreamingContext context)
        { 
            _wrappedCachedValue = EntityWrapperFactory.WrapEntityUsingContext(_cachedValue, ObjectContext);
        } 
 
        [OnSerializing()]
        [Browsable(false)] 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void OnSerializing(StreamingContext context)
        {
            if (!(WrappedOwner.Entity is IEntityWithRelationships)) 
            {
                throw new InvalidOperationException(System.Data.Entity.Strings.RelatedEnd_CannotSerialize("EntityReference")); 
            } 
        }
 
        #region Add

        /// 
        /// AddToLocalEnd is used by both APIs a) RelatedEnd.Add b) Value property setter. 
        /// ApplyConstraints is true in case of RelatedEnd.Add because one cannot add entity to ref it its already set
        /// however applyConstraints is false in case of Value property setter because value can be set to a new value 
        /// even if its non null. 
        /// 
        internal override void AddToLocalCache(IEntityWrapper wrappedEntity, bool applyConstraints) 
        {
            if (wrappedEntity != _wrappedCachedValue)
            {
                TransactionManager tm = ObjectContext != null ? ObjectContext.ObjectStateManager.TransactionManager : null; 
                if (applyConstraints && null != _wrappedCachedValue.Entity)
                { 
                    // The idea here is that we want to throw for constraint violations in things that we are bringing in, 
                    // but not when replacing references of things already in the context.  Therefore, if the the thing that
                    // we're replacing is in ProcessedEntities it means we're bringing it in and we should throw. 
                    if (tm == null || tm.ProcessedEntities == null || tm.ProcessedEntities.Contains(_wrappedCachedValue))
                    {
                        throw EntityUtil.CannotAddMoreThanOneEntityToEntityReference(this.RelationshipNavigation.To, this.RelationshipNavigation.RelationshipName);
                    } 
                }
                if (tm != null && wrappedEntity.Entity != null) 
                { 
                    // Setting this flag will prevent the FK from being temporarily set to null while changing
                    // it from one value to the next. 
                    tm.BeginRelatedEndAdd();
                }
                try
                { 
                    ClearCollectionOrRef(null, null, false);
                    _wrappedCachedValue = wrappedEntity; 
                    _cachedValue = (TEntity)wrappedEntity.Entity; 
                }
                finally 
                {
                    if (tm != null && tm.IsRelatedEndAdd)
                    {
                        tm.EndRelatedEndAdd(); 
                    }
                } 
            } 
        }
 
        internal override void AddToObjectCache(IEntityWrapper wrappedEntity)
        {
            // For POCO entities - set the CLR reference
            if (this.TargetAccessor.HasProperty) 
            {
                this.WrappedOwner.SetNavigationPropertyValue(this, wrappedEntity.Entity); 
            } 
        }
 
        #endregion
    }
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner       [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 

using System.Collections; 
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Common; 
using System.Data.Common.CommandTrees;
using System.Data.Objects.Internal; 
using System.Data.Metadata.Edm; 
using System.Diagnostics;
using System.Runtime.Serialization; 
using System.Linq;

namespace System.Data.Objects.DataClasses
{ 
    /// 
    /// Models a relationship end with multiplicity 1. 
    ///  
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
    [DataContract] 
    [Serializable]
    public sealed class EntityReference : EntityReference
        where TEntity : class
    { 
        // ------
        // Fields 
        // ------ 

        // 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.
        // Note that this field should no longer be used directly.  Instead, use the _wrappedCachedValue 
        // field.  This field is retained only for compatability with the serialization format introduced in v1.
        private TEntity _cachedValue; 
 
        [NonSerialized]
        private IEntityWrapper _wrappedCachedValue; 

        // ------------
        // Constructors
        // ------------ 

        ///  
        /// The default constructor is required for some serialization scenarios. It should not be used to 
        /// create new EntityReferences. Use the GetRelatedReference or GetRelatedEnd methods on the RelationshipManager
        /// class instead. 
        /// 
        public EntityReference()
        {
            _wrappedCachedValue = EntityWrapperFactory.NullWrapper; 
        }
 
        internal EntityReference(IEntityWrapper wrappedOwner, RelationshipNavigation navigation, IRelationshipFixer relationshipFixer) 
            : base(wrappedOwner, navigation, relationshipFixer)
        { 
            _wrappedCachedValue = EntityWrapperFactory.NullWrapper;
        }

        // ---------- 
        // Properties
        // ---------- 
 
        /// 
        /// Stub only please replace with actual implementation 
        /// 
        [System.Xml.Serialization.SoapIgnore]
        [System.Xml.Serialization.XmlIgnore]
        public TEntity Value 
        {
            get 
            { 
                DeferredLoad();
                return (TEntity)ReferenceValue.Entity; 
            }
            set
            {
                ReferenceValue = EntityWrapperFactory.WrapEntityUsingContext(value, ObjectContext); 
            }
        } 
 
        internal override IEntityWrapper CachedValue
        { 
            get { return _wrappedCachedValue; }
        }

        internal override IEntityWrapper ReferenceValue 
        {
            get 
            { 
                CheckOwnerNull();
                return _wrappedCachedValue; 
            }
            set
            {
                CheckOwnerNull(); 
                //setting to same value is a no-op (SQLBU DT # 446320)
                //setting to null is a special case because then we will also clear out any Added/Unchanged relationships with key entries, so we can't no-op if Value is null 
                if (value.Entity != null && value.Entity == _wrappedCachedValue.Entity) 
                {
                    return; 
                }

                if (null != value.Entity)
                { 
                    // Note that this is only done for the case where we are not setting the ref to null because
                    // clearing a ref is okay--it will cause the dependent to become deleted/detached. 
                    ValidateOwnerWithRIConstraints(value, value == EntityWrapperFactory.NullWrapper ? null : value.EntityKey, checkBothEnds: true); 
                    ObjectContext context = ObjectContext ?? value.Context;
                    if (context != null) 
                    {
                        context.ObjectStateManager.TransactionManager.EntityBeingReparented = GetDependentEndOfReferentialConstraint(value.Entity);
                    }
                    try 
                    {
                        Add(value, /*applyConstraints*/false); 
                    } 
                    finally
                    { 
                        if (context != null)
                        {
                            context.ObjectStateManager.TransactionManager.EntityBeingReparented = null;
                        } 
                    }
                } 
                else 
                {
                    if (UsingNoTracking) 
                    {
                        if (_wrappedCachedValue.Entity != null)
                        {
                            // The other end of relationship can be the EntityReference or EntityCollection 
                            // If the other end is EntityReference, its IsLoaded property should be set to FALSE
                            RelatedEnd relatedEnd = GetOtherEndOfRelationship(_wrappedCachedValue); 
                            relatedEnd.OnRelatedEndClear(); 
                        }
 
                        _isLoaded = false;
                    }
                    ClearCollectionOrRef(null, null, false);
                } 
            }
        } 
 
        // -------
        // Methods 
        // -------

        /// 
        /// Loads the related entity or entities into the local related end using the supplied MergeOption. 
        /// 
        public override void Load(MergeOption mergeOption) 
        { 
            CheckOwnerNull();
 
            // Validate that the Load is possible
            bool hasResults;
            ObjectQuery sourceQuery = ValidateLoad(mergeOption, "EntityReference", out hasResults);
 
            _suppressEvents = true; // we do not want any event during the bulk operation
            try 
            { 
                List refreshedValue = null;
                if (hasResults) 
                {
                    // Only issue a query if we know it can produce results (in the case of FK, there may not be any
                    // results).
                    refreshedValue = new List(GetResults(sourceQuery)); 
                }
                if (null == refreshedValue || refreshedValue.Count == 0) 
                { 
                    if (!((AssociationType)base.RelationMetadata).IsForeignKey && ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One)
                    { 
                        //query returned zero related end; one related end was expected.
                        throw EntityUtil.LessThanExpectedRelatedEntitiesFound();
                    }
                    else if (mergeOption == MergeOption.OverwriteChanges || mergeOption == MergeOption.PreserveChanges) 
                    {
                        // This entity is not related to anything in this AssociationSet and Role on the server. 
                        // If there is an existing _cachedValue, we may need to clear it out, based on the MergeOption 
                        EntityKey sourceKey = WrappedOwner.EntityKey;
                        EntityUtil.CheckEntityKeyNull(sourceKey); 
                        ObjectStateManager.RemoveRelationships(ObjectContext, mergeOption, (AssociationSet)RelationshipSet, sourceKey, (AssociationEndMember)FromEndProperty);
                    }
                    // else this is NoTracking or AppendOnly, and no entity was retrieved by the Load, so there's nothing extra to do
 
                    // Since we have no value and are not doing a merge, the last step is to set IsLoaded to true
                    _isLoaded = true; 
                } 
                else if (refreshedValue.Count == 1)
                { 
                    Merge(refreshedValue, mergeOption, true /*setIsLoaded*/);
                }
                else
                { 
                    // More than 1 result, which is non-recoverable data inconsistency
                    throw EntityUtil.MoreThanExpectedRelatedEntitiesFound(); 
                } 
            }
            finally 
            {
                _suppressEvents = false;
            }
            // fire the AssociationChange with Refresh 
            OnAssociationChanged(CollectionChangeAction.Refresh, null);
        } 
 
        /// 
        /// This operation is not allowed if the owner is null 
        /// 
        /// 
        internal override IEnumerable GetInternalEnumerable()
        { 
            if (ReferenceValue.Entity != null)
            { 
                yield return (TEntity)ReferenceValue.Entity; 
            }
        } 

        internal override IEnumerable GetWrappedEntities()
        {
            // 
            return _wrappedCachedValue.Entity == null ? new IEntityWrapper[0] : new IEntityWrapper[] { _wrappedCachedValue };
        } 
 
        /// 
        /// Attaches an entity to the EntityReference. The given 
        /// entity is not assumed to be the complete set of related entities.
        ///
        /// Owner and all entities passed in must be in Unchanged or Modified state.
        /// Deleted elements are allowed only when the state manager is already tracking the relationship 
        /// instance.
        ///  
        /// The entity to attach to the EntityCollection 
        /// Thrown when  is null.
        /// Thrown when the entity cannot be related via the current relationship end. 
        public void Attach(TEntity entity)
        {
            CheckOwnerNull();
            EntityUtil.CheckArgumentNull(entity, "entity"); 
            Attach(new IEntityWrapper[] { EntityWrapperFactory.WrapEntityUsingContext(entity, ObjectContext) }, false);
        } 
 
        internal override void Include(bool addRelationshipAsUnchanged, bool doAttach)
        { 
            Debug.Assert(this.ObjectContext != null, "Should not be trying to add entities to state manager if context is null");

            // If we have an actual value or a key for this reference, add it to the context
            if (null != _wrappedCachedValue.Entity) 
            {
                // Sometimes with mixed POCO and IPOCO, you can get different instances of IEntityWrappers stored in the IPOCO related ends 
                // These should be replaced by the IEntityWrapper that is stored in the context 
                IEntityWrapper identityWrapper = EntityWrapperFactory.WrapEntityUsingContext(_wrappedCachedValue.Entity, WrappedOwner.Context);
                if (identityWrapper != _wrappedCachedValue) 
                {
                    _wrappedCachedValue = identityWrapper;
                }
                IncludeEntity(_wrappedCachedValue, addRelationshipAsUnchanged, doAttach); 
            }
            else if (DetachedEntityKey != null) 
            { 
                IncludeEntityKey(doAttach);
            } 
            // else there is nothing to add for this relationship
        }

        private void IncludeEntityKey(bool doAttach) 
        {
            ObjectStateManager manager = this.ObjectContext.ObjectStateManager; 
 
            bool addNewRelationship = false;
            bool addKeyEntry = false; 
            EntityEntry existingEntry = manager.FindEntityEntry(DetachedEntityKey);
            if (existingEntry == null)
            {
                // add new key entry and create a relationship with it 
                addKeyEntry = true;
                addNewRelationship = true; 
            } 
            else
            { 
                if (existingEntry.IsKeyEntry)
                {
                    // We have an existing key entry, so just need to add a relationship with it
 
                    // We know the target end of this relationship is 1..1 or 0..1 since it is a reference, so if the source end is also not Many, we have a 1-to-1
                    if (FromEndProperty.RelationshipMultiplicity != RelationshipMultiplicity.Many) 
                    { 
                        // before we add a new relationship to this key entry, make sure it's not already related to something else
                        // We have to explicitly do this here because there are no other checks to make sure a key entry in a 1-to-1 doesn't end up in two of the same relationship 
                        foreach (RelationshipEntry relationshipEntry in this.ObjectContext.ObjectStateManager.FindRelationshipsByKey(DetachedEntityKey))
                        {
                            // only care about relationships in the same AssociationSet and where the key is playing the same role that it plays in this EntityReference
                            if (relationshipEntry.IsSameAssociationSetAndRole((AssociationSet)RelationshipSet, (AssociationEndMember)ToEndMember, DetachedEntityKey) && 
                                relationshipEntry.State != EntityState.Deleted)
                            { 
                                throw EntityUtil.EntityConflictsWithKeyEntry(); 
                            }
                        } 
                    }

                    addNewRelationship = true;
                } 
                else
                { 
                    IEntityWrapper wrappedTarget = existingEntry.WrappedEntity; 

                    // Verify that the target entity is in a valid state for adding a relationship 
                    if (existingEntry.State == EntityState.Deleted)
                    {
                        throw EntityUtil.UnableToAddRelationshipWithDeletedEntity();
                    } 

                    // We know the target end of this relationship is 1..1 or 0..1 since it is a reference, so if the source end is also not Many, we have a 1-to-1 
                    RelatedEnd relatedEnd = wrappedTarget.RelationshipManager.GetRelatedEndInternal(RelationshipName, RelationshipNavigation.From); 
                    if (FromEndProperty.RelationshipMultiplicity != RelationshipMultiplicity.Many && !relatedEnd.IsEmpty())
                    { 
                        // Make sure the target entity is not already related to something else.
                        // devnote: The call to Add below does *not* do this check for the fixup case, so if it's not done here, no failure will occur
                        //          and existing relationships may be deleted unexpectedly. RelatedEnd.Include should not remove existing relationships, only add new ones.
                        throw EntityUtil.EntityConflictsWithKeyEntry(); 
                    }
 
                    // We have an existing entity with the same key, just hook up the related ends 
                    this.Add(wrappedTarget,
                        applyConstraints: true, 
                        addRelationshipAsUnchanged: doAttach,
                        relationshipAlreadyExists: false,
                        allowModifyingOtherEndOfRelationship: true,
                        forceForeignKeyChanges: true); 

                    // add to the list of promoted key references so we can cleanup if a failure occurs later 
                    manager.TransactionManager.PopulatedEntityReferences.Add(this); 
                }
            } 

            // For FKs, don't create a key entry and don't create a relationship
            if (addNewRelationship && !IsForeignKey)
            { 
                // devnote: If we add any validation here, it needs to go here before adding the key entry,
                //          otherwise we have to clean up that entry if the validation fails 
 
                if (addKeyEntry)
                { 
                    EntitySet targetEntitySet = DetachedEntityKey.GetEntitySet(this.ObjectContext.MetadataWorkspace);
                    manager.AddKeyEntry(DetachedEntityKey, targetEntitySet);
                }
 
                EntityKey ownerKey = WrappedOwner.EntityKey;
                EntityUtil.CheckEntityKeyNull(ownerKey); 
                RelationshipWrapper wrapper = new RelationshipWrapper((AssociationSet)RelationshipSet, 
                    RelationshipNavigation.From, ownerKey, RelationshipNavigation.To, DetachedEntityKey);
                manager.AddNewRelation(wrapper, doAttach ? EntityState.Unchanged : EntityState.Added); 
            }
        }

        internal override void Exclude() 
        {
            Debug.Assert(this.ObjectContext != null, "Should not be trying to remove entities from state manager if context is null"); 
 
            if (null != _wrappedCachedValue.Entity)
            { 
                // It is possible that _cachedValue was originally null in this graph, but was only set
                // while the graph was being added, if the DetachedEntityKey matched its key. In that case,
                // we only want to clear _cachedValue and delete the relationship entry, but not remove the entity
                // itself from the context. 
                TransactionManager transManager = ObjectContext.ObjectStateManager.TransactionManager;
                bool doFullRemove = transManager.PopulatedEntityReferences.Contains(this); 
                bool doRelatedEndRemove = transManager.AlignedEntityReferences.Contains(this); 
                // For POCO, if the entity is undergoing snapshot for the first time, then in this step we actually
                // need to really exclude it rather than just disconnecting it.  If we don't, then it has the potential 
                // to remain in the context at the end of the rollback process.
                if ((transManager.ProcessedEntities == null || !transManager.ProcessedEntities.Contains(_wrappedCachedValue)) &&
                    (doFullRemove || doRelatedEndRemove))
                { 
                    // Retrieve the relationship entry before _cachedValue is set to null during Remove
                    RelationshipEntry relationshipEntry = IsForeignKey ? null : FindRelationshipEntryInObjectStateManager(_wrappedCachedValue); 
                    Debug.Assert(IsForeignKey || relationshipEntry != null, "Should have been able to find a valid relationship since _cachedValue is non-null"); 

                    // Remove the related ends and mark the relationship as deleted, but don't propagate the changes to the target entity itself 
                    Remove(_wrappedCachedValue,
                            doFixup: doFullRemove,
                            deleteEntity: false,
                            deleteOwner: false, 
                            applyReferentialConstraints: false,
                            preserveForeignKey: true); 
 
                    // The relationship will now either be detached (if it was previously in the Added state), or Deleted (if it was previously Unchanged)
                    // If it's Deleted, we need to AcceptChanges to get rid of it completely 
                    if (relationshipEntry != null && relationshipEntry.State != EntityState.Detached)
                    {
                        relationshipEntry.AcceptChanges();
                    } 

                    // Since this has been processed, remove it from the list 
                    if (doFullRemove) 
                    {
                        transManager.PopulatedEntityReferences.Remove(this); 
                    }
                    else
                    {
                        transManager.AlignedEntityReferences.Remove(this); 
                    }
                } 
                else 
                {
                    ExcludeEntity(_wrappedCachedValue); 
                }
            }
            else if (DetachedEntityKey != null)
            { 
                // there may still be relationship entries with stubs that need to be removed
                // this works whether we just added the key entry along with the relationship or if it was already existing 
                ExcludeEntityKey(); 
            }
            // else there is nothing to remove for this relationship 
        }

        private void ExcludeEntityKey()
        { 
            EntityKey ownerKey = WrappedOwner.EntityKey;
 
            RelationshipEntry relationshipEntry = this.ObjectContext.ObjectStateManager.FindRelationship(RelationshipSet, 
                new KeyValuePair(RelationshipNavigation.From, ownerKey),
                new KeyValuePair(RelationshipNavigation.To, DetachedEntityKey)); 

            // we may have failed in adding the graph before we actually added this relationship, so make sure we actually found one
            if (relationshipEntry != null)
            { 
                relationshipEntry.Delete(/*doFixup*/ false);
                // If entry was Added before, it is now Detached, otherwise AcceptChanges to detach it 
                if (relationshipEntry.State != EntityState.Detached) 
                {
                    relationshipEntry.AcceptChanges(); 
                }
            }
        }
 
        internal override void ClearCollectionOrRef(IEntityWrapper wrappedEntity, RelationshipNavigation navigation, bool doCascadeDelete)
        { 
            if (wrappedEntity == null) 
            {
                wrappedEntity = EntityWrapperFactory.NullWrapper; 
            }
            if (null != _wrappedCachedValue.Entity)
            {
                // Following condition checks if we have already visited this graph node. If its true then 
                // we should not do fixup because that would cause circular loop
                if ((wrappedEntity.Entity == _wrappedCachedValue.Entity) && (navigation.Equals(this.RelationshipNavigation))) 
                { 
                    Remove(_wrappedCachedValue, /*fixup*/false, /*deleteEntity*/false, /*deleteOwner*/false, /*applyReferentialConstraints*/false, /*preserveForeignKey*/false);
                } 
                else
                {
                    Remove(_wrappedCachedValue, /*fixup*/true, doCascadeDelete, /*deleteOwner*/false, /*applyReferentialConstraints*/true, /*preserveForeignKey*/false);
                } 
            }
            else 
            { 
                // this entity reference could be replacing a relationship that points to a key entry
                // we need to search relationships on the Owner entity to see if this is true, and if so remove the relationship entry 
                if (WrappedOwner.Entity != null && WrappedOwner.Context != null && !UsingNoTracking)
                {
                    EntityEntry ownerEntry = WrappedOwner.Context.ObjectStateManager.GetEntityEntry(WrappedOwner.Entity);
                    ownerEntry.DeleteRelationshipsThatReferenceKeys(this.RelationshipSet, this.ToEndMember); 
                }
            } 
 
            // If we have an Owner, clear the DetachedEntityKey.
            // If we do not have an owner, retain the key so that we can resolve the difference when the entity is attached to a context 
            if (this.WrappedOwner.Entity != null)
            {
                // Clear the detachedEntityKey as well. In cases where we have to fix up the detachedEntityKey, we will not always be able to detect
                // if we have *only* a Deleted relationship for a given entity/relationship/role, so clearing this here will ensure that 
                // even if no other relationships are added, the key value will still be correct.
                ((EntityReference)this).DetachedEntityKey = null; 
            } 
        }
 
        internal override void ClearWrappedValues()
        {
            this._cachedValue = null;
            this._wrappedCachedValue = NullEntityWrapper.NullWrapper; 
        }
 
        ///  
        ///
        ///  
        /// 
        /// 
        /// True if the verify succeeded, False if the Add should no-op
        internal override bool VerifyEntityForAdd(IEntityWrapper wrappedEntity, bool relationshipAlreadyExists) 
        {
            if (!relationshipAlreadyExists && this.ContainsEntity(wrappedEntity)) 
            { 
                return false;
            } 

            this.VerifyType(wrappedEntity);

            return true; 
        }
 
        internal override bool CanSetEntityType(IEntityWrapper wrappedEntity) 
        {
            return wrappedEntity.Entity is TEntity; 
        }

        internal override void VerifyType(IEntityWrapper wrappedEntity)
        { 
            if (!CanSetEntityType(wrappedEntity))
            { 
                throw EntityUtil.InvalidContainedTypeReference(wrappedEntity.Entity.GetType().FullName, typeof(TEntity).FullName); 
            }
        } 

        /// 
        /// Disconnected adds are not supported for an EntityReference so we should report this as an error.
        ///  
        /// The entity to add to the related end in a disconnected state.
        internal override void DisconnectedAdd(IEntityWrapper wrappedEntity) 
        { 
            CheckOwnerNull();
        } 

        /// 
        /// Disconnected removes are not supported for an EntityReference so we should report this as an error.
        ///  
        /// The entity to remove from the related end in a disconnected state.
        internal override bool DisconnectedRemove(IEntityWrapper wrappedEntity) 
        { 
            CheckOwnerNull();
            return false; 
        }

        /// 
        /// Remove from the RelatedEnd 
        /// 
        ///  
        ///  
        /// 
        internal override bool RemoveFromLocalCache(IEntityWrapper wrappedEntity, bool resetIsLoaded, bool preserveForeignKey) 
        {
            Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
            Debug.Assert(null == _wrappedCachedValue.Entity || wrappedEntity.Entity == _wrappedCachedValue.Entity, "The specified object is not a part of this relationship.");
 
            _wrappedCachedValue = EntityWrapperFactory.NullWrapper;
            _cachedValue = null; 
 
            if (resetIsLoaded)
            { 
                _isLoaded = false;
            }

            // This code sets nullable FK properties on a dependent end to null when a relationship has been nulled. 
            if (ObjectContext != null && IsForeignKey && !preserveForeignKey)
            { 
                NullAllForeignKeys(); 
            }
            return true; 
        }

        /// 
        /// Remove from the POCO collection 
        /// 
        ///  
        ///  
        internal override bool RemoveFromObjectCache(IEntityWrapper wrappedEntity)
        { 
            Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");

            // For POCO entities - clear the CLR reference
            if (this.TargetAccessor.HasProperty) 
            {
                this.WrappedOwner.RemoveNavigationPropertyValue(this, (TEntity)wrappedEntity.Entity); 
            } 

            return true; 
        }

        // Method used to retrieve properties from principal entities.
        // NOTE: 'properties' list is modified in this method and may already contains some properties. 
        internal override void RetrieveReferentialConstraintProperties(Dictionary> properties, HashSet visited)
        { 
            Debug.Assert(properties != null); 

            if (this._wrappedCachedValue.Entity != null) 
            {
                // Dictionary< propertyName, >
                Dictionary> retrievedProperties;
 
                // PERFORMANCE: ReferentialConstraints collection in typical scenario is very small (1-3 elements)
                foreach (ReferentialConstraint constraint in ((AssociationType)this.RelationMetadata).ReferentialConstraints) 
                { 
                    if (constraint.ToRole == FromEndProperty)
                    { 
                        // Detect circular references
                        if (visited.Contains(_wrappedCachedValue))
                        {
                            throw EntityUtil.CircularRelationshipsWithReferentialConstraints(); 
                        }
                        visited.Add(_wrappedCachedValue); 
 
                        _wrappedCachedValue.RelationshipManager.RetrieveReferentialConstraintProperties(out retrievedProperties, visited, includeOwnValues: true);
 
                        Debug.Assert(retrievedProperties != null);
                        Debug.Assert(constraint.FromProperties.Count == constraint.ToProperties.Count, "Referential constraints From/To properties list have different size");

                        // Following loop rewrites properties from "retrievedProperties" into "properties". 
                        // At the same time, property's name is translated from name from principal end into name from dependent end:
                        // Example: Client - Order 
                        //          Client is principal end, Order is dependent end, Client.C_ID == Order.Client_ID 
                        // Input : retrievedProperties = { "C_ID" = 123 }
                        // Output: properties = { "Client_ID" = 123 } 

                        // NOTE order of properties in collections constraint.From/ToProperties is important
                        for (int i = 0; i < constraint.FromProperties.Count; ++i)
                        { 
                            EntityEntry.AddOrIncreaseCounter(
                                    properties, 
                                    constraint.ToProperties[i].Name, 
                                    retrievedProperties[constraint.FromProperties[i].Name].Key);
                        } 
                    }
                }
            }
        } 

        internal override bool IsEmpty() 
        { 
            return _wrappedCachedValue.Entity == null;
        } 

        internal override void VerifyMultiplicityConstraintsForAdd(bool applyConstraints)
        {
            if (applyConstraints && !this.IsEmpty()) 
            {
                throw EntityUtil.CannotAddMoreThanOneEntityToEntityReference(this.RelationshipNavigation.To, this.RelationshipNavigation.RelationshipName); 
            } 
        }
 
        // Update IsLoaded flag if necessary
        // This method is called when Clear() was called on the other end of relationship (if the other end is EntityCollection)
        // or when Value property of the other end was set to null (if the other end is EntityReference).
        // This method is used only when NoTracking option was used. 
        internal override void OnRelatedEndClear()
        { 
            // If other end of relationship was loaded, it mean that this end was also cleared. 
            _isLoaded = false;
        } 

        internal override bool ContainsEntity(IEntityWrapper wrappedEntity)
        {
            // Using operator 'as' instead of () allows calling ContainsEntity 
            // with entity of different type than TEntity.
            return null != _wrappedCachedValue.Entity && _wrappedCachedValue.Entity == wrappedEntity.Entity; 
        } 

        // Identical code is in EntityCollection, but this can't be moved to the base class because it relies on the 
        // knowledge of the generic type, and the base class isn't generic
        public ObjectQuery CreateSourceQuery()
        {
            CheckOwnerNull(); 
            bool hasResults;
            return CreateSourceQuery(DefaultMergeOption, out hasResults); 
        } 

        internal override IEnumerable CreateSourceQueryInternal() 
        {
            return CreateSourceQuery();
        }
        //End identical code 

        ///  
        /// Take any values in the incoming RelatedEnd and sets them onto the values 
        /// that currently exist in this RelatedEnd
        ///  
        /// 
        internal void InitializeWithValue(RelatedEnd relatedEnd)
        {
            Debug.Assert(this._wrappedCachedValue.Entity == null, "The EntityReference already has a value."); 
            EntityReference reference = relatedEnd as EntityReference;
            if (reference != null && reference._wrappedCachedValue.Entity != null) 
            { 
                _wrappedCachedValue = reference._wrappedCachedValue;
                _cachedValue = (TEntity)_wrappedCachedValue.Entity; 
            }
        }

        internal override bool CheckIfNavigationPropertyContainsEntity(IEntityWrapper wrapper) 
        {
            Debug.Assert(this.RelationshipNavigation != null, "null RelationshipNavigation"); 
 
            // If the navigation property doesn't exist (e.g. uni-directional prop), then it can't contain the entity.
            if (!TargetAccessor.HasProperty) 
            {
                return false;
            }
 
            object value = this.WrappedOwner.GetNavigationPropertyValue(this);
 
            return Object.Equals(value, wrapper.Entity); 
        }
 
        internal override void VerifyNavigationPropertyForAdd(IEntityWrapper wrapper)
        {
            if (this.TargetAccessor.HasProperty)
            { 
                object value = WrappedOwner.GetNavigationPropertyValue(this);
                if (!Object.ReferenceEquals(null, value) && !Object.Equals(value, wrapper.Entity)) 
                { 
                    throw EntityUtil.CannotAddMoreThanOneEntityToEntityReference(
                        this.RelationshipNavigation.To, this.RelationshipNavigation.RelationshipName); 
                }
            }
        }
 
        // This method is required to maintain compatability with the v1 binary serialization format.
        // In particular, it recreates a entity wrapper from the serialized cached value. 
        // 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 OnRefDeserialized(StreamingContext context)
        { 
            _wrappedCachedValue = EntityWrapperFactory.WrapEntityUsingContext(_cachedValue, ObjectContext);
        } 
 
        [OnSerializing()]
        [Browsable(false)] 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void OnSerializing(StreamingContext context)
        {
            if (!(WrappedOwner.Entity is IEntityWithRelationships)) 
            {
                throw new InvalidOperationException(System.Data.Entity.Strings.RelatedEnd_CannotSerialize("EntityReference")); 
            } 
        }
 
        #region Add

        /// 
        /// AddToLocalEnd is used by both APIs a) RelatedEnd.Add b) Value property setter. 
        /// ApplyConstraints is true in case of RelatedEnd.Add because one cannot add entity to ref it its already set
        /// however applyConstraints is false in case of Value property setter because value can be set to a new value 
        /// even if its non null. 
        /// 
        internal override void AddToLocalCache(IEntityWrapper wrappedEntity, bool applyConstraints) 
        {
            if (wrappedEntity != _wrappedCachedValue)
            {
                TransactionManager tm = ObjectContext != null ? ObjectContext.ObjectStateManager.TransactionManager : null; 
                if (applyConstraints && null != _wrappedCachedValue.Entity)
                { 
                    // The idea here is that we want to throw for constraint violations in things that we are bringing in, 
                    // but not when replacing references of things already in the context.  Therefore, if the the thing that
                    // we're replacing is in ProcessedEntities it means we're bringing it in and we should throw. 
                    if (tm == null || tm.ProcessedEntities == null || tm.ProcessedEntities.Contains(_wrappedCachedValue))
                    {
                        throw EntityUtil.CannotAddMoreThanOneEntityToEntityReference(this.RelationshipNavigation.To, this.RelationshipNavigation.RelationshipName);
                    } 
                }
                if (tm != null && wrappedEntity.Entity != null) 
                { 
                    // Setting this flag will prevent the FK from being temporarily set to null while changing
                    // it from one value to the next. 
                    tm.BeginRelatedEndAdd();
                }
                try
                { 
                    ClearCollectionOrRef(null, null, false);
                    _wrappedCachedValue = wrappedEntity; 
                    _cachedValue = (TEntity)wrappedEntity.Entity; 
                }
                finally 
                {
                    if (tm != null && tm.IsRelatedEndAdd)
                    {
                        tm.EndRelatedEndAdd(); 
                    }
                } 
            } 
        }
 
        internal override void AddToObjectCache(IEntityWrapper wrappedEntity)
        {
            // For POCO entities - set the CLR reference
            if (this.TargetAccessor.HasProperty) 
            {
                this.WrappedOwner.SetNavigationPropertyValue(this, wrappedEntity.Entity); 
            } 
        }
 
        #endregion
    }
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK