EntityReference.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.cs / 1305376 / EntityReference.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.Data.Common.Utils;
using System.Linq;

namespace System.Data.Objects.DataClasses 
{
    ///  
    /// Models a relationship end with multiplicity 1. 
    /// 
    [DataContract] 
    [Serializable]
    public abstract class EntityReference : RelatedEnd
    {
        // ------ 
        // 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.

        // The following field is valid only for detached EntityReferences, see EntityKey property for more details. 
        private EntityKey _detachedEntityKey = null;
 
        // The following field is used to cache the FK value to the principal for FK relationships. 
        // It is okay to not serialize this field because it is only used when the entity is tracked.
        // For a detached entity it can always be null and cause no problems. 
        [NonSerialized]
        private EntityKey _cachedForeignKey;

        // ------------ 
        // 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.
        /// 
        internal EntityReference() 
        {
        } 
 
        internal EntityReference(IEntityWrapper wrappedOwner, RelationshipNavigation navigation, IRelationshipFixer relationshipFixer)
            : base(wrappedOwner, navigation, relationshipFixer) 
        {
        }

        ///  
        /// Returns the EntityKey of the target entity associated with this EntityReference.
        /// 
        /// Is non-null in the following scenarios: 
        /// (a) Entities are tracked by a context and an Unchanged or Added client-side relationships exists for this EntityReference's owner with the
        ///     same RelationshipName and source role. This relationship could have been created explicitly by the user (e.g. by setting 
        ///     the EntityReference.Value, setting this property directly, or by calling EntityCollection.Add) or automatically through span queries.
        /// (b) If the EntityKey was non-null before detaching an entity from the context, it will still be non-null after detaching, until any operation
        ///     occurs that would set it to null, as described below.
        /// (c) Entities are detached and the EntityKey is explicitly set to non-null by the user. 
        /// (d) Entity graph was created using a NoTracking query with full span
        /// 
        /// Is null in the following scenarios: 
        /// (a) Entities are tracked by a context but there is no Unchanged or Added client-side relationship for this EntityReference's owner with the
        ///     same RelationshipName and source role. 
        /// (b) Entities are tracked by a context and a relationship exists, but the target entity has a temporary key (i.e. it is Added) or the key
        ///     is one of the special keys
        /// (c) Entities are detached and the relationship was explicitly created by the user.
        ///  
        [DataMember]
        public EntityKey EntityKey 
        { 
            // This is the only scenario where it is valid to have a null Owner, so don't check it
 
            get
            {
                if (this.ObjectContext != null && !UsingNoTracking)
                { 
                    Debug.Assert(this.WrappedOwner.Entity != null, "Unexpected null Owner on EntityReference attached to a context");
 
                    EntityKey attachedKey = null; 

                    // If this EntityReference contains an entity, look up the key on that object 
                    if (CachedValue.Entity != null)
                    {
                        // While processing an attach the owner may have a context while the target does not.  This means
                        // that the target may gave an entity but not yet have an attached entity key. 
                        attachedKey = CachedValue.EntityKey;
                        if (attachedKey != null && !IsValidEntityKeyType(attachedKey)) 
                        { 
                            // don't return temporary or special keys from this property
                            attachedKey = null; 
                        }
                    }
                    else
                    { 
                        if (IsForeignKey)
                        { 
                            // For dependent ends, return the value of the cached foreign key if it is not conceptually null 
                            if (IsDependentEndOfReferentialConstraint(false) && _cachedForeignKey != null)
                            { 
                                if (!ForeignKeyFactory.IsConceptualNullKey(_cachedForeignKey))
                                {
                                    attachedKey = _cachedForeignKey;
                                } 
                            }
                            else 
                            { 
                                // Principal ends or ends that haven't been fixed up yet (i.e during Add/Attach) should use the DetachedEntityKey value
                                // that contains the last known value that was set 
                                attachedKey = DetachedEntityKey;
                            }
                        }
                        else 
                        {
                            // There could still be an Added or Unchanged relationship with a stub entry 
                            EntityKey ownerKey = WrappedOwner.EntityKey; 
                            foreach (RelationshipEntry relationshipEntry in this.ObjectContext.ObjectStateManager.FindRelationshipsByKey(ownerKey))
                            { 
                                // We only care about the relationships that match the AssociationSet and source role for the owner of this EntityReference
                                if (relationshipEntry.State != EntityState.Deleted &&
                                    relationshipEntry.IsSameAssociationSetAndRole((AssociationSet)RelationshipSet, (AssociationEndMember)this.FromEndProperty, ownerKey))
                                { 
                                    Debug.Assert(attachedKey == null, "Found more than one non-Deleted relationship for the same AssociationSet and source role");
                                    attachedKey = relationshipEntry.RelationshipWrapper.GetOtherEntityKey(ownerKey); 
                                    // key should never be temporary or special since it came from a key entry 
                                }
                            } 
                        }
                    }
                    Debug.Assert(attachedKey == null || IsValidEntityKeyType(attachedKey),
                        "Unexpected temporary or special key"); 
                    return attachedKey;
                } 
                else 
                {
                    return DetachedEntityKey; 
                }
            }
            set
            { 
                SetEntityKey(value, forceFixup: false);
            } 
        } 

        internal void SetEntityKey(EntityKey value, bool forceFixup) 
        {
            if (value != null && value == EntityKey && (ReferenceValue.Entity != null || (ReferenceValue.Entity == null && !forceFixup)))
            {
                // "no-op" -- this is not really no-op in the attached case, because at a minimum we have to do a key lookup, 
                // worst case we have to review all relationships for the owner entity
                // However, if we don't do this, we can get into a scenario where we are setting the key to the same thing it's already set to 
                // and this could have side effects, especially with RI constraints and cascade delete. We don't want to delete something 
                // and then add it back, if that deleting could have additional unexpected effects. Don't bother doing this check if value is
                // null, because EntityKey could be null even if there are Added/Unchanged relationships, if the target entity has a temporary key. 
                // In that case, we still need to delete that existing relationship, so it's not a no-op
                return;
            }
 
            if (this.ObjectContext != null && !UsingNoTracking)
            { 
                Debug.Assert(this.WrappedOwner.Entity != null, "Unexpected null Owner on EntityReference attached to a context"); 

                // null is a valid value for the EntityKey, but temporary and special keys are not 
                // devnote: Can't check this on detached references because this property could be set to a temp key during deserialization,
                //          if the key hasn't finished deserializing yet.
                if (value != null && !IsValidEntityKeyType(value))
                { 
                    throw EntityUtil.CannotSetSpecialKeys();
                } 
 
                if (value == null)
                { 
                    // For unloaded FK relationships in the context we attempt to null FK values here.
                    if (ReferenceValue.Entity == null &&
                        WrappedOwner.Entity != null &&
                        WrappedOwner.Context != null && 
                        !UsingNoTracking &&
                        IsForeignKey) 
                    { 
                        // For identifying relationships, we throw, since we cannot set primary key values to null.
                        if (IsDependentEndOfReferentialConstraint(checkIdentifying: true)) 
                        {
                            throw EntityUtil.CannotChangeReferentialConstraintProperty();
                        }
                        // Otherwise, we try to set FK properties to null, which may result in conceptual nulls being set. 
                        RemoveFromLocalCache(EntityWrapperFactory.NullWrapper, resetIsLoaded: true, preserveForeignKey: false);
                        DetachedEntityKey = null; 
                    } 
                    else
                    { 
                        // delegate to Value property for all validation and firing of events
                        this.ReferenceValue = EntityWrapperFactory.NullWrapper;
                    }
                } 
                else
                { 
                    // Verify that the key has the right EntitySet for this RelationshipSet 
                    EntitySet targetEntitySet = value.GetEntitySet(ObjectContext.MetadataWorkspace);
                    CheckRelationEntitySet(targetEntitySet); 
                    value.ValidateEntityKey(targetEntitySet, true /*isArgumentException */, "value");

                    ObjectStateManager manager = this.ObjectContext.ObjectStateManager;
 
                    // If we already have an entry with this key, we just need to create a relationship with it
                    bool addNewRelationship = false; 
                    // If we don't already have any matching entries for this key, we'll have to create a new entry 
                    bool addKeyEntry = false;
                    EntityEntry targetEntry = manager.FindEntityEntry(value); 
                    if (targetEntry != null)
                    {
                        // If it's not a key entry, just use the entity to set this reference's Value
                        if (!targetEntry.IsKeyEntry) 
                        {
                            // Delegate to the Value property to clear any existing relationship 
                            // and to add the new one. This will fire the appropriate events and 
                            // ensure that the related ends are connected.
 
                            // It has to be a TEntity since we already verified that the EntitySet is correct above
                            this.ReferenceValue = targetEntry.WrappedEntity;
                        }
                        else 
                        {
                            // if the existing entry is a key entry, we just need to 
                            // add a new relationship between the source entity and that key 
                            addNewRelationship = true;
                        } 
                    }
                    else
                    {
                        // no entry exists, so we'll need to add a key along with the relationship 
                        addKeyEntry = !IsForeignKey;
                        addNewRelationship = true; 
                    } 

                    if (addNewRelationship) 
                    {
                        EntityKey ownerKey = ValidateOwnerWithRIConstraints(targetEntry == null ? null : targetEntry.WrappedEntity, value, checkBothEnds: true);

                        // Verify that the owner is in a valid state for adding a relationship 
                        ValidateStateForAdd(this.WrappedOwner);
 
                        if (addKeyEntry) 
                        {
                            manager.AddKeyEntry(value, targetEntitySet); 
                        }

                        // First, clear any existing relationships
                        manager.TransactionManager.EntityBeingReparented = WrappedOwner.Entity; 
                        try
                        { 
                            ClearCollectionOrRef(null, null, /*doCascadeDelete*/ false); 
                        }
                        finally 
                        {
                            manager.TransactionManager.EntityBeingReparented = null;
                        }
 
                        // Then add the new one
                        if (IsForeignKey) 
                        { 
                            DetachedEntityKey = value;
                            // Update the FK values in this entity 
                            if (IsDependentEndOfReferentialConstraint(false))
                            {
                                UpdateForeignKeyValues(WrappedOwner, value);
                            } 
                        }
                        else 
                        { 
                            RelationshipWrapper wrapper = new RelationshipWrapper((AssociationSet)RelationshipSet, RelationshipNavigation.From, ownerKey, RelationshipNavigation.To, value);
                            // Add the relationship in the unchanged state if 
                            EntityState relationshipState = EntityState.Added;

                            // If this is an unchanged/modified dependent end of a relationship and we are allowing the EntityKey to be set
                            // create the relationship in the Unchanged state because the state must "match" the dependent end state 
                            if (!ownerKey.IsTemporary && IsDependentEndOfReferentialConstraint(false))
                            { 
                                relationshipState = EntityState.Unchanged; 
                            }
                            manager.AddNewRelation(wrapper, relationshipState); 
                        }
                    }
                }
            } 
            else
            { 
                // Just set the field for detached object -- during Attach/Add we will make sure this value 
                // is not in conflict if the EntityReference contains a real entity. We cannot always determine the
                // EntityKey for any real entity in the detached state, so we don't bother to do it here. 
                DetachedEntityKey = value;
            }
        }
 
        internal EntityKey AttachedEntityKey
        { 
            get 
            {
                Debug.Assert(this.ObjectContext != null && !UsingNoTracking, "Should only need to access AttachedEntityKey property on attached EntityReferences"); 
                return this.EntityKey;
            }
        }
 
        internal EntityKey DetachedEntityKey
        { 
            get 
            {
                return _detachedEntityKey; 
            }
            set
            {
                _detachedEntityKey = value; 
            }
        } 
 
        internal EntityKey CachedForeignKey
        { 
            get
            {
                return EntityKey ?? _cachedForeignKey;
            } 
        }
 
        internal void SetCachedForeignKey(EntityKey newForeignKey, EntityEntry source) 
        {
            if (this.ObjectContext != null && this.ObjectContext.ObjectStateManager != null &&  // are we attached? 
                source != null && // do we have an entry?
                _cachedForeignKey != null && !ForeignKeyFactory.IsConceptualNullKey(_cachedForeignKey) // do we have an fk?
                && _cachedForeignKey != newForeignKey) // is the FK different from the one that we already have?
            { 
                this.ObjectContext.ObjectStateManager.RemoveEntryFromForeignKeyIndex(_cachedForeignKey, source);
            } 
            _cachedForeignKey = newForeignKey; 
        }
 
        internal IEnumerable GetAllKeyValues()
        {
            if (EntityKey != null)
            { 
                yield return EntityKey;
            } 
 
            if (_cachedForeignKey != null)
            { 
                yield return _cachedForeignKey;
            }

            if (_detachedEntityKey != null) 
            {
                yield return _detachedEntityKey; 
            } 
        }
 
        internal abstract IEntityWrapper CachedValue
        {
            get;
        } 

        internal abstract IEntityWrapper ReferenceValue 
        { 
            get;
            set; 
        }


        internal EntityKey ValidateOwnerWithRIConstraints(IEntityWrapper targetEntity, EntityKey targetEntityKey, bool checkBothEnds) 
        {
            EntityKey ownerKey = WrappedOwner.EntityKey; 
 
            // Check if Referential Constraints are violated
            if ((object)ownerKey != null && 
                !ownerKey.IsTemporary &&
                IsDependentEndOfReferentialConstraint(checkIdentifying: true))
            {
                Debug.Assert(CachedForeignKey != null || EntityKey == null, "CachedForeignKey should not be null if EntityKey is not null."); 
                ValidateSettingRIConstraints(targetEntity,
                                             targetEntityKey == null, 
                                             (this.CachedForeignKey != null && this.CachedForeignKey != targetEntityKey)); 
            }
            else if (checkBothEnds && targetEntity != null && targetEntity.Entity != null) 
            {
                EntityReference otherEnd = GetOtherEndOfRelationship(targetEntity) as EntityReference;
                if (otherEnd != null)
                { 
                    otherEnd.ValidateOwnerWithRIConstraints(WrappedOwner, ownerKey, checkBothEnds: false);
                } 
            } 

 
            return ownerKey;
        }

        internal void ValidateSettingRIConstraints(IEntityWrapper targetEntity, bool settingToNull, bool changingForeignKeyValue) 
        {
            if (settingToNull ||                    // setting the principle to null 
                changingForeignKeyValue ||          // existing key does not match incoming key 
                (targetEntity != null && (targetEntity.ObjectStateEntry == null ||  // setting to a detached principle
                                         (EntityKey == null && targetEntity.ObjectStateEntry.State == EntityState.Deleted || // setting to a deleted principle 
                                         (CachedForeignKey == null && targetEntity.ObjectStateEntry.State == EntityState.Added)))))    // setting to an added principle
            {
                throw EntityUtil.CannotChangeReferentialConstraintProperty();
            } 
        }
 
        ///  
        /// EntityReferences can only deferred load if they are empty
        ///  
        internal override bool CanDeferredLoad
        {
            get
            { 
                return IsEmpty();
            } 
        } 

        ///  
        /// Takes key values from the given principal entity and transfers them to the foreign key properties
        /// of the dependant entry.  This method requires a context, but does not require that either
        /// entity is in the context.  This allows it to work in NoTracking cases where we have the context
        /// but we're not tracked by that context. 
        /// 
        /// The entity into which foreign key values will be written 
        /// The entity from which key values will be obtained 
        /// If non-null, then keeps track of FKs that have already been set such that an exception can be thrown if we find conflicting values
        /// If true, then the property setter is called even if FK values already match, 
        ///                           which causes the FK properties to be marked as modified.
        internal void UpdateForeignKeyValues(IEntityWrapper dependentEntity, IEntityWrapper principalEntity, Dictionary changedFKs, bool forceChange)
        {
            Debug.Assert(dependentEntity.Entity != null, "dependentEntity.Entity == null"); 
            Debug.Assert(principalEntity.Entity != null, "principalEntity.Entity == null");
            Debug.Assert(this.IsForeignKey, "cannot update foreign key values if the relationship is not a FK"); 
            ReferentialConstraint constraint = ((AssociationType)this.RelationMetadata).ReferentialConstraints[0]; 
            Debug.Assert(constraint != null, "null constraint");
 
            bool isUnchangedDependent = (object)WrappedOwner.EntityKey != null &&
                                        !WrappedOwner.EntityKey.IsTemporary &&
                                        IsDependentEndOfReferentialConstraint(checkIdentifying: true);
 
            ObjectStateManager stateManager = ObjectContext.ObjectStateManager;
            stateManager.TransactionManager.BeginForeignKeyUpdate(this); 
            try 
            {
                EntitySet principalEntitySet = ((AssociationSet)RelationshipSet).AssociationSetEnds[ToEndMember.Name].EntitySet; 
                StateManagerTypeMetadata principalTypeMetadata = stateManager.GetOrAddStateManagerTypeMetadata(principalEntity.IdentityType, principalEntitySet);

                EntitySet dependentEntitySet = ((AssociationSet)RelationshipSet).AssociationSetEnds[FromEndProperty.Name].EntitySet;
                StateManagerTypeMetadata dependentTypeMetadata = stateManager.GetOrAddStateManagerTypeMetadata(dependentEntity.IdentityType, dependentEntitySet); 

                var principalProps = constraint.FromProperties; 
                int numValues = principalProps.Count; 
                string[] keyNames = null;
                object[] values = null; 
                if (numValues > 1)
                {
                    keyNames = principalEntitySet.ElementType.KeyMemberNames;
                    values = new object[numValues]; 
                }
                for (int i = 0; i < numValues; i++) 
                { 
                    int principalOrdinal = principalTypeMetadata.GetOrdinalforOLayerMemberName(principalProps[i].Name);
                    object value = principalTypeMetadata.Member(principalOrdinal).GetValue(principalEntity.Entity); 
                    int dependentOrdinal = dependentTypeMetadata.GetOrdinalforOLayerMemberName(constraint.ToProperties[i].Name);
                    bool valueChanging = !ByValueEqualityComparer.Default.Equals(dependentTypeMetadata.Member(dependentOrdinal).GetValue(dependentEntity.Entity), value);
                    if (forceChange || valueChanging)
                    { 
                        if (isUnchangedDependent)
                        { 
                            ValidateSettingRIConstraints(principalEntity, settingToNull: value != null, changingForeignKeyValue: valueChanging); 
                        }
                        // If we're tracking FK values that have already been set, then compare the value we are about to set 
                        // to the value we previously set for this ordinal, if such a value exists.  If they don't match then
                        // it means that we got conflicting FK values from two different PKs and we should throw.
                        if (changedFKs != null)
                        { 
                            object previouslySetValue;
                            if (changedFKs.TryGetValue(dependentOrdinal, out previouslySetValue)) 
                            { 
                                if (!ByValueEqualityComparer.Default.Equals(previouslySetValue, value))
                                { 
                                    throw new InvalidOperationException(System.Data.Entity.Strings.Update_ReferentialConstraintIntegrityViolation);
                                }
                            }
                            else 
                            {
                                changedFKs[dependentOrdinal] = value; 
                            } 
                        }
                        dependentEntity.SetCurrentValue( 
                            dependentEntity.ObjectStateEntry,
                            dependentTypeMetadata.Member(dependentOrdinal),
                            -1,
                            dependentEntity.Entity, 
                            value);
                    } 
 
                    if (numValues > 1)
                    { 
                        int keyIndex = Array.IndexOf(keyNames, principalProps[i].Name);
                        Debug.Assert(keyIndex >= 0 && keyIndex < numValues, "Could not find constraint prop name in entity set key names");
                        values[keyIndex] = value;
                    } 
                    else
                    { 
                        SetCachedForeignKey(new EntityKey(principalEntitySet, value), dependentEntity.ObjectStateEntry); 
                    }
                } 

                if (numValues > 1)
                {
                    SetCachedForeignKey(new EntityKey(principalEntitySet, values), dependentEntity.ObjectStateEntry); 
                }
                if (WrappedOwner.ObjectStateEntry != null) 
                { 
                    stateManager.ForgetEntryWithConceptualNull(WrappedOwner.ObjectStateEntry, resetAllKeys: false);
                } 
            }
            finally
            {
                stateManager.TransactionManager.EndForeignKeyUpdate(); 
            }
        } 
 
        /// 
        /// Takes key values from the given principal key and transfers them to the foreign key properties 
        /// of the dependant entry.  This method requires a context, but does not require that either
        /// entity or key is in the context.  This allows it to work in NoTracking cases where we have the context
        /// but we're not tracked by that context.
        ///  
        /// The entity into which foreign key values will be written
        /// The key from which key values will be obtained 
        internal void UpdateForeignKeyValues(IEntityWrapper dependentEntity, EntityKey principalKey) 
        {
            Debug.Assert(dependentEntity.Entity != null, "dependentEntity.Entity == null"); 
            Debug.Assert(principalKey != null, "principalKey == null");
            Debug.Assert(!principalKey.IsTemporary, "Cannot update from a temp key");
            Debug.Assert(this.IsForeignKey, "cannot update foreign key values if the relationship is not a FK");
            ReferentialConstraint constraint = ((AssociationType)this.RelationMetadata).ReferentialConstraints[0]; 
            Debug.Assert(constraint != null, "null constraint");
 
            ObjectStateManager stateManager = ObjectContext.ObjectStateManager; 
            stateManager.TransactionManager.BeginForeignKeyUpdate(this);
            try 
            {
                EntitySet dependentEntitySet = ((AssociationSet)RelationshipSet).AssociationSetEnds[FromEndProperty.Name].EntitySet;
                StateManagerTypeMetadata dependentTypeMetadata = stateManager.GetOrAddStateManagerTypeMetadata(dependentEntity.IdentityType, dependentEntitySet);
 
                for (int i = 0; i < constraint.FromProperties.Count; i++)
                { 
                    object value = principalKey.FindValueByName(constraint.FromProperties[i].Name); 
                    int dependentOrdinal = dependentTypeMetadata.GetOrdinalforOLayerMemberName(constraint.ToProperties[i].Name);
                    object currentValue = dependentTypeMetadata.Member(dependentOrdinal).GetValue(dependentEntity.Entity); 
                    if (!ByValueEqualityComparer.Default.Equals(currentValue, value))
                    {
                        dependentEntity.SetCurrentValue(
                            dependentEntity.ObjectStateEntry, 
                            dependentTypeMetadata.Member(dependentOrdinal),
                            -1, 
                            dependentEntity.Entity, 
                            value);
                    } 
                }

                SetCachedForeignKey(principalKey, dependentEntity.ObjectStateEntry);
                if (WrappedOwner.ObjectStateEntry != null) 
                {
                    stateManager.ForgetEntryWithConceptualNull(WrappedOwner.ObjectStateEntry, resetAllKeys: false); 
                } 
            }
            finally 
            {
                stateManager.TransactionManager.EndForeignKeyUpdate();
            }
        } 

        internal object GetDependentEndOfReferentialConstraint(object relatedValue) 
        { 
            return IsDependentEndOfReferentialConstraint(checkIdentifying: false) ?
                WrappedOwner.Entity : 
                relatedValue;
        }

        internal bool NavigationPropertyIsNullOrMissing() 
        {
            Debug.Assert(RelationshipNavigation != null, "null RelationshipNavigation"); 
 
            return !TargetAccessor.HasProperty || WrappedOwner.GetNavigationPropertyValue(this) == null;
        } 

        /// 
        /// Attempts to null all FKs associated with the dependent end of this relationship on this entity.
        /// This may result in setting conceptual nulls if the FK is not nullable. 
        /// 
        internal void NullAllForeignKeys() 
        { 
            Debug.Assert(ObjectContext != null, "Nulling FKs only works when attached.");
            Debug.Assert(IsForeignKey, "Cannot null FKs for independent associations."); 

            ObjectStateManager stateManager = ObjectContext.ObjectStateManager;
            EntityEntry entry = WrappedOwner.ObjectStateEntry;
            TransactionManager transManager = stateManager.TransactionManager; 
            if (!transManager.IsGraphUpdate && !transManager.IsAttachTracking && !transManager.IsRelatedEndAdd)
            { 
                ReferentialConstraint constraint = ((AssociationType)RelationMetadata).ReferentialConstraints.Single(); 
                if (TargetRoleName == constraint.FromRole.Name) // Only do this on the dependent end
                { 
                    if (transManager.IsDetaching)
                    {
                        // If the principal is being detached, then the dependent must be added back to the
                        // dangling keys index. 
                        // Perf note: The dependent currently gets added when it is being detached and is then
                        // removed again later in the process.  The code could be optomized to prevent this. 
                        Debug.Assert(entry != null, "State entry must exist while detaching."); 
                        EntityKey foreignKey = ForeignKeyFactory.CreateKeyFromForeignKeyValues(entry, this);
                        if (foreignKey != null) 
                        {
                            stateManager.AddEntryContainingForeignKeyToIndex(foreignKey, entry);
                        }
                    } 
                    else if (!stateManager.InFKSetter && !transManager.IsForeignKeyUpdate)
                    { 
                        transManager.BeginForeignKeyUpdate(this); 
                        try
                        { 
                            bool unableToNull = true;
                            bool canSetModifiedProps = entry != null && (entry.State == EntityState.Modified || entry.State == EntityState.Unchanged);
                            EntitySet dependentEntitySet = ((AssociationSet)RelationshipSet).AssociationSetEnds[FromEndProperty.Name].EntitySet;
                            StateManagerTypeMetadata dependentTypeMetadata = stateManager.GetOrAddStateManagerTypeMetadata(WrappedOwner.IdentityType, dependentEntitySet); 

                            for (int i = 0; i < constraint.FromProperties.Count; i++) 
                            { 
                                string propertyName = constraint.ToProperties[i].Name;
                                int dependentOrdinal = dependentTypeMetadata.GetOrdinalforOLayerMemberName(propertyName); 
                                StateManagerMemberMetadata member = dependentTypeMetadata.Member(dependentOrdinal);
                                if (member.ClrMetadata.Nullable)
                                {
                                    // Only set the value to null if it is not already null. 
                                    if (member.GetValue(WrappedOwner.Entity) != null)
                                    { 
                                        WrappedOwner.SetCurrentValue( 
                                        WrappedOwner.ObjectStateEntry,
                                        dependentTypeMetadata.Member(dependentOrdinal), 
                                        -1,
                                        WrappedOwner.Entity,
                                        null);
                                    } 
                                    unableToNull = false;
                                } 
                                else if (canSetModifiedProps) 
                                {
                                    entry.SetModifiedProperty(propertyName); 
                                }
                            }
                            if (unableToNull)
                            { 
                                // We were unable to null out the FK because all FK properties were non-nullable.
                                // We need to keep track of this state so that we treat the FK as null even though 
                                // we were not able to null it.  This prevents the FK from being used for fixup and 
                                // also causes an exception to be thrown if an attempt is made to commit in this state.
 
                                //We should only set a conceptual null if the entity is tracked
                                if (entry != null)
                                {
                                    //The CachedForeignKey may be null if we are putting 
                                    //back a Conceptual Null as part of roll back
                                    EntityKey realKey = CachedForeignKey; 
                                    if (realKey == null) 
                                    {
                                        realKey = ForeignKeyFactory.CreateKeyFromForeignKeyValues(entry, this); 
                                    }

                                    SetCachedForeignKey(ForeignKeyFactory.CreateConceptualNullKey(realKey), entry);
                                    stateManager.RememberEntryWithConceptualNull(entry); 
                                }
                            } 
                            else 
                            {
                                SetCachedForeignKey(null, entry); 
                            }
                        }
                        finally
                        { 
                            transManager.EndForeignKeyUpdate();
                        } 
                    } 
                }
            } 
        }
    }
}

// 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.Data.Common.Utils;
using System.Linq;

namespace System.Data.Objects.DataClasses 
{
    ///  
    /// Models a relationship end with multiplicity 1. 
    /// 
    [DataContract] 
    [Serializable]
    public abstract class EntityReference : RelatedEnd
    {
        // ------ 
        // 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.

        // The following field is valid only for detached EntityReferences, see EntityKey property for more details. 
        private EntityKey _detachedEntityKey = null;
 
        // The following field is used to cache the FK value to the principal for FK relationships. 
        // It is okay to not serialize this field because it is only used when the entity is tracked.
        // For a detached entity it can always be null and cause no problems. 
        [NonSerialized]
        private EntityKey _cachedForeignKey;

        // ------------ 
        // 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.
        /// 
        internal EntityReference() 
        {
        } 
 
        internal EntityReference(IEntityWrapper wrappedOwner, RelationshipNavigation navigation, IRelationshipFixer relationshipFixer)
            : base(wrappedOwner, navigation, relationshipFixer) 
        {
        }

        ///  
        /// Returns the EntityKey of the target entity associated with this EntityReference.
        /// 
        /// Is non-null in the following scenarios: 
        /// (a) Entities are tracked by a context and an Unchanged or Added client-side relationships exists for this EntityReference's owner with the
        ///     same RelationshipName and source role. This relationship could have been created explicitly by the user (e.g. by setting 
        ///     the EntityReference.Value, setting this property directly, or by calling EntityCollection.Add) or automatically through span queries.
        /// (b) If the EntityKey was non-null before detaching an entity from the context, it will still be non-null after detaching, until any operation
        ///     occurs that would set it to null, as described below.
        /// (c) Entities are detached and the EntityKey is explicitly set to non-null by the user. 
        /// (d) Entity graph was created using a NoTracking query with full span
        /// 
        /// Is null in the following scenarios: 
        /// (a) Entities are tracked by a context but there is no Unchanged or Added client-side relationship for this EntityReference's owner with the
        ///     same RelationshipName and source role. 
        /// (b) Entities are tracked by a context and a relationship exists, but the target entity has a temporary key (i.e. it is Added) or the key
        ///     is one of the special keys
        /// (c) Entities are detached and the relationship was explicitly created by the user.
        ///  
        [DataMember]
        public EntityKey EntityKey 
        { 
            // This is the only scenario where it is valid to have a null Owner, so don't check it
 
            get
            {
                if (this.ObjectContext != null && !UsingNoTracking)
                { 
                    Debug.Assert(this.WrappedOwner.Entity != null, "Unexpected null Owner on EntityReference attached to a context");
 
                    EntityKey attachedKey = null; 

                    // If this EntityReference contains an entity, look up the key on that object 
                    if (CachedValue.Entity != null)
                    {
                        // While processing an attach the owner may have a context while the target does not.  This means
                        // that the target may gave an entity but not yet have an attached entity key. 
                        attachedKey = CachedValue.EntityKey;
                        if (attachedKey != null && !IsValidEntityKeyType(attachedKey)) 
                        { 
                            // don't return temporary or special keys from this property
                            attachedKey = null; 
                        }
                    }
                    else
                    { 
                        if (IsForeignKey)
                        { 
                            // For dependent ends, return the value of the cached foreign key if it is not conceptually null 
                            if (IsDependentEndOfReferentialConstraint(false) && _cachedForeignKey != null)
                            { 
                                if (!ForeignKeyFactory.IsConceptualNullKey(_cachedForeignKey))
                                {
                                    attachedKey = _cachedForeignKey;
                                } 
                            }
                            else 
                            { 
                                // Principal ends or ends that haven't been fixed up yet (i.e during Add/Attach) should use the DetachedEntityKey value
                                // that contains the last known value that was set 
                                attachedKey = DetachedEntityKey;
                            }
                        }
                        else 
                        {
                            // There could still be an Added or Unchanged relationship with a stub entry 
                            EntityKey ownerKey = WrappedOwner.EntityKey; 
                            foreach (RelationshipEntry relationshipEntry in this.ObjectContext.ObjectStateManager.FindRelationshipsByKey(ownerKey))
                            { 
                                // We only care about the relationships that match the AssociationSet and source role for the owner of this EntityReference
                                if (relationshipEntry.State != EntityState.Deleted &&
                                    relationshipEntry.IsSameAssociationSetAndRole((AssociationSet)RelationshipSet, (AssociationEndMember)this.FromEndProperty, ownerKey))
                                { 
                                    Debug.Assert(attachedKey == null, "Found more than one non-Deleted relationship for the same AssociationSet and source role");
                                    attachedKey = relationshipEntry.RelationshipWrapper.GetOtherEntityKey(ownerKey); 
                                    // key should never be temporary or special since it came from a key entry 
                                }
                            } 
                        }
                    }
                    Debug.Assert(attachedKey == null || IsValidEntityKeyType(attachedKey),
                        "Unexpected temporary or special key"); 
                    return attachedKey;
                } 
                else 
                {
                    return DetachedEntityKey; 
                }
            }
            set
            { 
                SetEntityKey(value, forceFixup: false);
            } 
        } 

        internal void SetEntityKey(EntityKey value, bool forceFixup) 
        {
            if (value != null && value == EntityKey && (ReferenceValue.Entity != null || (ReferenceValue.Entity == null && !forceFixup)))
            {
                // "no-op" -- this is not really no-op in the attached case, because at a minimum we have to do a key lookup, 
                // worst case we have to review all relationships for the owner entity
                // However, if we don't do this, we can get into a scenario where we are setting the key to the same thing it's already set to 
                // and this could have side effects, especially with RI constraints and cascade delete. We don't want to delete something 
                // and then add it back, if that deleting could have additional unexpected effects. Don't bother doing this check if value is
                // null, because EntityKey could be null even if there are Added/Unchanged relationships, if the target entity has a temporary key. 
                // In that case, we still need to delete that existing relationship, so it's not a no-op
                return;
            }
 
            if (this.ObjectContext != null && !UsingNoTracking)
            { 
                Debug.Assert(this.WrappedOwner.Entity != null, "Unexpected null Owner on EntityReference attached to a context"); 

                // null is a valid value for the EntityKey, but temporary and special keys are not 
                // devnote: Can't check this on detached references because this property could be set to a temp key during deserialization,
                //          if the key hasn't finished deserializing yet.
                if (value != null && !IsValidEntityKeyType(value))
                { 
                    throw EntityUtil.CannotSetSpecialKeys();
                } 
 
                if (value == null)
                { 
                    // For unloaded FK relationships in the context we attempt to null FK values here.
                    if (ReferenceValue.Entity == null &&
                        WrappedOwner.Entity != null &&
                        WrappedOwner.Context != null && 
                        !UsingNoTracking &&
                        IsForeignKey) 
                    { 
                        // For identifying relationships, we throw, since we cannot set primary key values to null.
                        if (IsDependentEndOfReferentialConstraint(checkIdentifying: true)) 
                        {
                            throw EntityUtil.CannotChangeReferentialConstraintProperty();
                        }
                        // Otherwise, we try to set FK properties to null, which may result in conceptual nulls being set. 
                        RemoveFromLocalCache(EntityWrapperFactory.NullWrapper, resetIsLoaded: true, preserveForeignKey: false);
                        DetachedEntityKey = null; 
                    } 
                    else
                    { 
                        // delegate to Value property for all validation and firing of events
                        this.ReferenceValue = EntityWrapperFactory.NullWrapper;
                    }
                } 
                else
                { 
                    // Verify that the key has the right EntitySet for this RelationshipSet 
                    EntitySet targetEntitySet = value.GetEntitySet(ObjectContext.MetadataWorkspace);
                    CheckRelationEntitySet(targetEntitySet); 
                    value.ValidateEntityKey(targetEntitySet, true /*isArgumentException */, "value");

                    ObjectStateManager manager = this.ObjectContext.ObjectStateManager;
 
                    // If we already have an entry with this key, we just need to create a relationship with it
                    bool addNewRelationship = false; 
                    // If we don't already have any matching entries for this key, we'll have to create a new entry 
                    bool addKeyEntry = false;
                    EntityEntry targetEntry = manager.FindEntityEntry(value); 
                    if (targetEntry != null)
                    {
                        // If it's not a key entry, just use the entity to set this reference's Value
                        if (!targetEntry.IsKeyEntry) 
                        {
                            // Delegate to the Value property to clear any existing relationship 
                            // and to add the new one. This will fire the appropriate events and 
                            // ensure that the related ends are connected.
 
                            // It has to be a TEntity since we already verified that the EntitySet is correct above
                            this.ReferenceValue = targetEntry.WrappedEntity;
                        }
                        else 
                        {
                            // if the existing entry is a key entry, we just need to 
                            // add a new relationship between the source entity and that key 
                            addNewRelationship = true;
                        } 
                    }
                    else
                    {
                        // no entry exists, so we'll need to add a key along with the relationship 
                        addKeyEntry = !IsForeignKey;
                        addNewRelationship = true; 
                    } 

                    if (addNewRelationship) 
                    {
                        EntityKey ownerKey = ValidateOwnerWithRIConstraints(targetEntry == null ? null : targetEntry.WrappedEntity, value, checkBothEnds: true);

                        // Verify that the owner is in a valid state for adding a relationship 
                        ValidateStateForAdd(this.WrappedOwner);
 
                        if (addKeyEntry) 
                        {
                            manager.AddKeyEntry(value, targetEntitySet); 
                        }

                        // First, clear any existing relationships
                        manager.TransactionManager.EntityBeingReparented = WrappedOwner.Entity; 
                        try
                        { 
                            ClearCollectionOrRef(null, null, /*doCascadeDelete*/ false); 
                        }
                        finally 
                        {
                            manager.TransactionManager.EntityBeingReparented = null;
                        }
 
                        // Then add the new one
                        if (IsForeignKey) 
                        { 
                            DetachedEntityKey = value;
                            // Update the FK values in this entity 
                            if (IsDependentEndOfReferentialConstraint(false))
                            {
                                UpdateForeignKeyValues(WrappedOwner, value);
                            } 
                        }
                        else 
                        { 
                            RelationshipWrapper wrapper = new RelationshipWrapper((AssociationSet)RelationshipSet, RelationshipNavigation.From, ownerKey, RelationshipNavigation.To, value);
                            // Add the relationship in the unchanged state if 
                            EntityState relationshipState = EntityState.Added;

                            // If this is an unchanged/modified dependent end of a relationship and we are allowing the EntityKey to be set
                            // create the relationship in the Unchanged state because the state must "match" the dependent end state 
                            if (!ownerKey.IsTemporary && IsDependentEndOfReferentialConstraint(false))
                            { 
                                relationshipState = EntityState.Unchanged; 
                            }
                            manager.AddNewRelation(wrapper, relationshipState); 
                        }
                    }
                }
            } 
            else
            { 
                // Just set the field for detached object -- during Attach/Add we will make sure this value 
                // is not in conflict if the EntityReference contains a real entity. We cannot always determine the
                // EntityKey for any real entity in the detached state, so we don't bother to do it here. 
                DetachedEntityKey = value;
            }
        }
 
        internal EntityKey AttachedEntityKey
        { 
            get 
            {
                Debug.Assert(this.ObjectContext != null && !UsingNoTracking, "Should only need to access AttachedEntityKey property on attached EntityReferences"); 
                return this.EntityKey;
            }
        }
 
        internal EntityKey DetachedEntityKey
        { 
            get 
            {
                return _detachedEntityKey; 
            }
            set
            {
                _detachedEntityKey = value; 
            }
        } 
 
        internal EntityKey CachedForeignKey
        { 
            get
            {
                return EntityKey ?? _cachedForeignKey;
            } 
        }
 
        internal void SetCachedForeignKey(EntityKey newForeignKey, EntityEntry source) 
        {
            if (this.ObjectContext != null && this.ObjectContext.ObjectStateManager != null &&  // are we attached? 
                source != null && // do we have an entry?
                _cachedForeignKey != null && !ForeignKeyFactory.IsConceptualNullKey(_cachedForeignKey) // do we have an fk?
                && _cachedForeignKey != newForeignKey) // is the FK different from the one that we already have?
            { 
                this.ObjectContext.ObjectStateManager.RemoveEntryFromForeignKeyIndex(_cachedForeignKey, source);
            } 
            _cachedForeignKey = newForeignKey; 
        }
 
        internal IEnumerable GetAllKeyValues()
        {
            if (EntityKey != null)
            { 
                yield return EntityKey;
            } 
 
            if (_cachedForeignKey != null)
            { 
                yield return _cachedForeignKey;
            }

            if (_detachedEntityKey != null) 
            {
                yield return _detachedEntityKey; 
            } 
        }
 
        internal abstract IEntityWrapper CachedValue
        {
            get;
        } 

        internal abstract IEntityWrapper ReferenceValue 
        { 
            get;
            set; 
        }


        internal EntityKey ValidateOwnerWithRIConstraints(IEntityWrapper targetEntity, EntityKey targetEntityKey, bool checkBothEnds) 
        {
            EntityKey ownerKey = WrappedOwner.EntityKey; 
 
            // Check if Referential Constraints are violated
            if ((object)ownerKey != null && 
                !ownerKey.IsTemporary &&
                IsDependentEndOfReferentialConstraint(checkIdentifying: true))
            {
                Debug.Assert(CachedForeignKey != null || EntityKey == null, "CachedForeignKey should not be null if EntityKey is not null."); 
                ValidateSettingRIConstraints(targetEntity,
                                             targetEntityKey == null, 
                                             (this.CachedForeignKey != null && this.CachedForeignKey != targetEntityKey)); 
            }
            else if (checkBothEnds && targetEntity != null && targetEntity.Entity != null) 
            {
                EntityReference otherEnd = GetOtherEndOfRelationship(targetEntity) as EntityReference;
                if (otherEnd != null)
                { 
                    otherEnd.ValidateOwnerWithRIConstraints(WrappedOwner, ownerKey, checkBothEnds: false);
                } 
            } 

 
            return ownerKey;
        }

        internal void ValidateSettingRIConstraints(IEntityWrapper targetEntity, bool settingToNull, bool changingForeignKeyValue) 
        {
            if (settingToNull ||                    // setting the principle to null 
                changingForeignKeyValue ||          // existing key does not match incoming key 
                (targetEntity != null && (targetEntity.ObjectStateEntry == null ||  // setting to a detached principle
                                         (EntityKey == null && targetEntity.ObjectStateEntry.State == EntityState.Deleted || // setting to a deleted principle 
                                         (CachedForeignKey == null && targetEntity.ObjectStateEntry.State == EntityState.Added)))))    // setting to an added principle
            {
                throw EntityUtil.CannotChangeReferentialConstraintProperty();
            } 
        }
 
        ///  
        /// EntityReferences can only deferred load if they are empty
        ///  
        internal override bool CanDeferredLoad
        {
            get
            { 
                return IsEmpty();
            } 
        } 

        ///  
        /// Takes key values from the given principal entity and transfers them to the foreign key properties
        /// of the dependant entry.  This method requires a context, but does not require that either
        /// entity is in the context.  This allows it to work in NoTracking cases where we have the context
        /// but we're not tracked by that context. 
        /// 
        /// The entity into which foreign key values will be written 
        /// The entity from which key values will be obtained 
        /// If non-null, then keeps track of FKs that have already been set such that an exception can be thrown if we find conflicting values
        /// If true, then the property setter is called even if FK values already match, 
        ///                           which causes the FK properties to be marked as modified.
        internal void UpdateForeignKeyValues(IEntityWrapper dependentEntity, IEntityWrapper principalEntity, Dictionary changedFKs, bool forceChange)
        {
            Debug.Assert(dependentEntity.Entity != null, "dependentEntity.Entity == null"); 
            Debug.Assert(principalEntity.Entity != null, "principalEntity.Entity == null");
            Debug.Assert(this.IsForeignKey, "cannot update foreign key values if the relationship is not a FK"); 
            ReferentialConstraint constraint = ((AssociationType)this.RelationMetadata).ReferentialConstraints[0]; 
            Debug.Assert(constraint != null, "null constraint");
 
            bool isUnchangedDependent = (object)WrappedOwner.EntityKey != null &&
                                        !WrappedOwner.EntityKey.IsTemporary &&
                                        IsDependentEndOfReferentialConstraint(checkIdentifying: true);
 
            ObjectStateManager stateManager = ObjectContext.ObjectStateManager;
            stateManager.TransactionManager.BeginForeignKeyUpdate(this); 
            try 
            {
                EntitySet principalEntitySet = ((AssociationSet)RelationshipSet).AssociationSetEnds[ToEndMember.Name].EntitySet; 
                StateManagerTypeMetadata principalTypeMetadata = stateManager.GetOrAddStateManagerTypeMetadata(principalEntity.IdentityType, principalEntitySet);

                EntitySet dependentEntitySet = ((AssociationSet)RelationshipSet).AssociationSetEnds[FromEndProperty.Name].EntitySet;
                StateManagerTypeMetadata dependentTypeMetadata = stateManager.GetOrAddStateManagerTypeMetadata(dependentEntity.IdentityType, dependentEntitySet); 

                var principalProps = constraint.FromProperties; 
                int numValues = principalProps.Count; 
                string[] keyNames = null;
                object[] values = null; 
                if (numValues > 1)
                {
                    keyNames = principalEntitySet.ElementType.KeyMemberNames;
                    values = new object[numValues]; 
                }
                for (int i = 0; i < numValues; i++) 
                { 
                    int principalOrdinal = principalTypeMetadata.GetOrdinalforOLayerMemberName(principalProps[i].Name);
                    object value = principalTypeMetadata.Member(principalOrdinal).GetValue(principalEntity.Entity); 
                    int dependentOrdinal = dependentTypeMetadata.GetOrdinalforOLayerMemberName(constraint.ToProperties[i].Name);
                    bool valueChanging = !ByValueEqualityComparer.Default.Equals(dependentTypeMetadata.Member(dependentOrdinal).GetValue(dependentEntity.Entity), value);
                    if (forceChange || valueChanging)
                    { 
                        if (isUnchangedDependent)
                        { 
                            ValidateSettingRIConstraints(principalEntity, settingToNull: value != null, changingForeignKeyValue: valueChanging); 
                        }
                        // If we're tracking FK values that have already been set, then compare the value we are about to set 
                        // to the value we previously set for this ordinal, if such a value exists.  If they don't match then
                        // it means that we got conflicting FK values from two different PKs and we should throw.
                        if (changedFKs != null)
                        { 
                            object previouslySetValue;
                            if (changedFKs.TryGetValue(dependentOrdinal, out previouslySetValue)) 
                            { 
                                if (!ByValueEqualityComparer.Default.Equals(previouslySetValue, value))
                                { 
                                    throw new InvalidOperationException(System.Data.Entity.Strings.Update_ReferentialConstraintIntegrityViolation);
                                }
                            }
                            else 
                            {
                                changedFKs[dependentOrdinal] = value; 
                            } 
                        }
                        dependentEntity.SetCurrentValue( 
                            dependentEntity.ObjectStateEntry,
                            dependentTypeMetadata.Member(dependentOrdinal),
                            -1,
                            dependentEntity.Entity, 
                            value);
                    } 
 
                    if (numValues > 1)
                    { 
                        int keyIndex = Array.IndexOf(keyNames, principalProps[i].Name);
                        Debug.Assert(keyIndex >= 0 && keyIndex < numValues, "Could not find constraint prop name in entity set key names");
                        values[keyIndex] = value;
                    } 
                    else
                    { 
                        SetCachedForeignKey(new EntityKey(principalEntitySet, value), dependentEntity.ObjectStateEntry); 
                    }
                } 

                if (numValues > 1)
                {
                    SetCachedForeignKey(new EntityKey(principalEntitySet, values), dependentEntity.ObjectStateEntry); 
                }
                if (WrappedOwner.ObjectStateEntry != null) 
                { 
                    stateManager.ForgetEntryWithConceptualNull(WrappedOwner.ObjectStateEntry, resetAllKeys: false);
                } 
            }
            finally
            {
                stateManager.TransactionManager.EndForeignKeyUpdate(); 
            }
        } 
 
        /// 
        /// Takes key values from the given principal key and transfers them to the foreign key properties 
        /// of the dependant entry.  This method requires a context, but does not require that either
        /// entity or key is in the context.  This allows it to work in NoTracking cases where we have the context
        /// but we're not tracked by that context.
        ///  
        /// The entity into which foreign key values will be written
        /// The key from which key values will be obtained 
        internal void UpdateForeignKeyValues(IEntityWrapper dependentEntity, EntityKey principalKey) 
        {
            Debug.Assert(dependentEntity.Entity != null, "dependentEntity.Entity == null"); 
            Debug.Assert(principalKey != null, "principalKey == null");
            Debug.Assert(!principalKey.IsTemporary, "Cannot update from a temp key");
            Debug.Assert(this.IsForeignKey, "cannot update foreign key values if the relationship is not a FK");
            ReferentialConstraint constraint = ((AssociationType)this.RelationMetadata).ReferentialConstraints[0]; 
            Debug.Assert(constraint != null, "null constraint");
 
            ObjectStateManager stateManager = ObjectContext.ObjectStateManager; 
            stateManager.TransactionManager.BeginForeignKeyUpdate(this);
            try 
            {
                EntitySet dependentEntitySet = ((AssociationSet)RelationshipSet).AssociationSetEnds[FromEndProperty.Name].EntitySet;
                StateManagerTypeMetadata dependentTypeMetadata = stateManager.GetOrAddStateManagerTypeMetadata(dependentEntity.IdentityType, dependentEntitySet);
 
                for (int i = 0; i < constraint.FromProperties.Count; i++)
                { 
                    object value = principalKey.FindValueByName(constraint.FromProperties[i].Name); 
                    int dependentOrdinal = dependentTypeMetadata.GetOrdinalforOLayerMemberName(constraint.ToProperties[i].Name);
                    object currentValue = dependentTypeMetadata.Member(dependentOrdinal).GetValue(dependentEntity.Entity); 
                    if (!ByValueEqualityComparer.Default.Equals(currentValue, value))
                    {
                        dependentEntity.SetCurrentValue(
                            dependentEntity.ObjectStateEntry, 
                            dependentTypeMetadata.Member(dependentOrdinal),
                            -1, 
                            dependentEntity.Entity, 
                            value);
                    } 
                }

                SetCachedForeignKey(principalKey, dependentEntity.ObjectStateEntry);
                if (WrappedOwner.ObjectStateEntry != null) 
                {
                    stateManager.ForgetEntryWithConceptualNull(WrappedOwner.ObjectStateEntry, resetAllKeys: false); 
                } 
            }
            finally 
            {
                stateManager.TransactionManager.EndForeignKeyUpdate();
            }
        } 

        internal object GetDependentEndOfReferentialConstraint(object relatedValue) 
        { 
            return IsDependentEndOfReferentialConstraint(checkIdentifying: false) ?
                WrappedOwner.Entity : 
                relatedValue;
        }

        internal bool NavigationPropertyIsNullOrMissing() 
        {
            Debug.Assert(RelationshipNavigation != null, "null RelationshipNavigation"); 
 
            return !TargetAccessor.HasProperty || WrappedOwner.GetNavigationPropertyValue(this) == null;
        } 

        /// 
        /// Attempts to null all FKs associated with the dependent end of this relationship on this entity.
        /// This may result in setting conceptual nulls if the FK is not nullable. 
        /// 
        internal void NullAllForeignKeys() 
        { 
            Debug.Assert(ObjectContext != null, "Nulling FKs only works when attached.");
            Debug.Assert(IsForeignKey, "Cannot null FKs for independent associations."); 

            ObjectStateManager stateManager = ObjectContext.ObjectStateManager;
            EntityEntry entry = WrappedOwner.ObjectStateEntry;
            TransactionManager transManager = stateManager.TransactionManager; 
            if (!transManager.IsGraphUpdate && !transManager.IsAttachTracking && !transManager.IsRelatedEndAdd)
            { 
                ReferentialConstraint constraint = ((AssociationType)RelationMetadata).ReferentialConstraints.Single(); 
                if (TargetRoleName == constraint.FromRole.Name) // Only do this on the dependent end
                { 
                    if (transManager.IsDetaching)
                    {
                        // If the principal is being detached, then the dependent must be added back to the
                        // dangling keys index. 
                        // Perf note: The dependent currently gets added when it is being detached and is then
                        // removed again later in the process.  The code could be optomized to prevent this. 
                        Debug.Assert(entry != null, "State entry must exist while detaching."); 
                        EntityKey foreignKey = ForeignKeyFactory.CreateKeyFromForeignKeyValues(entry, this);
                        if (foreignKey != null) 
                        {
                            stateManager.AddEntryContainingForeignKeyToIndex(foreignKey, entry);
                        }
                    } 
                    else if (!stateManager.InFKSetter && !transManager.IsForeignKeyUpdate)
                    { 
                        transManager.BeginForeignKeyUpdate(this); 
                        try
                        { 
                            bool unableToNull = true;
                            bool canSetModifiedProps = entry != null && (entry.State == EntityState.Modified || entry.State == EntityState.Unchanged);
                            EntitySet dependentEntitySet = ((AssociationSet)RelationshipSet).AssociationSetEnds[FromEndProperty.Name].EntitySet;
                            StateManagerTypeMetadata dependentTypeMetadata = stateManager.GetOrAddStateManagerTypeMetadata(WrappedOwner.IdentityType, dependentEntitySet); 

                            for (int i = 0; i < constraint.FromProperties.Count; i++) 
                            { 
                                string propertyName = constraint.ToProperties[i].Name;
                                int dependentOrdinal = dependentTypeMetadata.GetOrdinalforOLayerMemberName(propertyName); 
                                StateManagerMemberMetadata member = dependentTypeMetadata.Member(dependentOrdinal);
                                if (member.ClrMetadata.Nullable)
                                {
                                    // Only set the value to null if it is not already null. 
                                    if (member.GetValue(WrappedOwner.Entity) != null)
                                    { 
                                        WrappedOwner.SetCurrentValue( 
                                        WrappedOwner.ObjectStateEntry,
                                        dependentTypeMetadata.Member(dependentOrdinal), 
                                        -1,
                                        WrappedOwner.Entity,
                                        null);
                                    } 
                                    unableToNull = false;
                                } 
                                else if (canSetModifiedProps) 
                                {
                                    entry.SetModifiedProperty(propertyName); 
                                }
                            }
                            if (unableToNull)
                            { 
                                // We were unable to null out the FK because all FK properties were non-nullable.
                                // We need to keep track of this state so that we treat the FK as null even though 
                                // we were not able to null it.  This prevents the FK from being used for fixup and 
                                // also causes an exception to be thrown if an attempt is made to commit in this state.
 
                                //We should only set a conceptual null if the entity is tracked
                                if (entry != null)
                                {
                                    //The CachedForeignKey may be null if we are putting 
                                    //back a Conceptual Null as part of roll back
                                    EntityKey realKey = CachedForeignKey; 
                                    if (realKey == null) 
                                    {
                                        realKey = ForeignKeyFactory.CreateKeyFromForeignKeyValues(entry, this); 
                                    }

                                    SetCachedForeignKey(ForeignKeyFactory.CreateConceptualNullKey(realKey), entry);
                                    stateManager.RememberEntryWithConceptualNull(entry); 
                                }
                            } 
                            else 
                            {
                                SetCachedForeignKey(null, entry); 
                            }
                        }
                        finally
                        { 
                            transManager.EndForeignKeyUpdate();
                        } 
                    } 
                }
            } 
        }
    }
}

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