/// <summary>
        /// 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.
        /// </summary>
        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 optimized 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 (!ReferenceEquals(stateManager.EntityInvokingFKSetter, WrappedOwner.Entity) && !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);

                                // This is a check for nullability in o-space. However, o-space nullability is not the
                                // same as nullability of the underlying type. In particular, one difference is that when
                                // attribute-based mapping is used then a property can be marked as not nullable in o-space
                                // even when the underlying CLR type is nullable. For such a case, we treat the property
                                // as if it were not nullable (since that's what we have shipped) even though we could
                                // technically set it to null.
                                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);
                                    }
                                    else
                                    {
                                        // Given that the current value is null, this next check confirms that the original
                                        // value is also null.  If it isn't, then we must make sure that the entity is marked
                                        // as modified.
                                        // This case can happen because fixup in the entity can set the FK to null while processing
                                        // a RelatedEnd operation.  This will be detected by DetectChanges, but when performing
                                        // RelatedEnd operations the user is not required to call DetectChanges.
                                        if (canSetModifiedProps && WrappedOwner.ObjectStateEntry.OriginalValues.GetValue(dependentOrdinal) != null)
                                        {
                                            entry.SetModifiedProperty(propertyName);
                                        }
                                    }
                                    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);
                                    }

                                    // Note that the realKey can still be null here for a situation where the key is marked not nullable
                                    // in o-space and yet the underlying type is nullable and the entity has been added or attached with a null
                                    // value for the property. This will cause SaveChanges to throw unless the entity is marked
                                    // as deleted before SaveChanges is called, in which case we don't want to set a conceptual
                                    // null here as the call might very well succeed in the database since, unless the FK is
                                    // a concurrency token, the value we have for it is not used at all for the delete.
                                    if (realKey != null)
                                    {
                                        SetCachedForeignKey(ForeignKeyFactory.CreateConceptualNullKey(realKey), entry);
                                        stateManager.RememberEntryWithConceptualNull(entry);
                                    }
                                }
                            }
                            else
                            {
                                SetCachedForeignKey(null, entry);
                            }
                        }
                        finally
                        {
                            transManager.EndForeignKeyUpdate();
                        }
                    }
                }
            }
        }
Exemple #2
0
        internal void NullAllForeignKeys()
        {
            ObjectStateManager objectStateManager = this.ObjectContext.ObjectStateManager;
            EntityEntry        objectStateEntry   = this.WrappedOwner.ObjectStateEntry;
            TransactionManager transactionManager = objectStateManager.TransactionManager;

            if (transactionManager.IsGraphUpdate || transactionManager.IsAttachTracking || transactionManager.IsRelatedEndAdd)
            {
                return;
            }
            ReferentialConstraint referentialConstraint = ((AssociationType)this.RelationMetadata).ReferentialConstraints.Single <ReferentialConstraint>();

            if (!(this.TargetRoleName == referentialConstraint.FromRole.Name))
            {
                return;
            }
            if (transactionManager.IsDetaching)
            {
                EntityKey foreignKeyValues = ForeignKeyFactory.CreateKeyFromForeignKeyValues(objectStateEntry, (RelatedEnd)this);
                if (!(foreignKeyValues != (EntityKey)null))
                {
                    return;
                }
                objectStateManager.AddEntryContainingForeignKeyToIndex(this, foreignKeyValues, objectStateEntry);
            }
            else
            {
                if (object.ReferenceEquals(objectStateManager.EntityInvokingFKSetter, this.WrappedOwner.Entity) || transactionManager.IsForeignKeyUpdate)
                {
                    return;
                }
                transactionManager.BeginForeignKeyUpdate(this);
                try
                {
                    bool      flag1     = true;
                    bool      flag2     = objectStateEntry != null && (objectStateEntry.State == EntityState.Modified || objectStateEntry.State == EntityState.Unchanged);
                    EntitySet entitySet = ((AssociationSet)this.RelationshipSet).AssociationSetEnds[this.FromEndMember.Name].EntitySet;
                    StateManagerTypeMetadata managerTypeMetadata = objectStateManager.GetOrAddStateManagerTypeMetadata(this.WrappedOwner.IdentityType, entitySet);
                    for (int index = 0; index < referentialConstraint.FromProperties.Count; ++index)
                    {
                        string name             = referentialConstraint.ToProperties[index].Name;
                        int    olayerMemberName = managerTypeMetadata.GetOrdinalforOLayerMemberName(name);
                        StateManagerMemberMetadata managerMemberMetadata = managerTypeMetadata.Member(olayerMemberName);
                        if (managerMemberMetadata.ClrMetadata.Nullable)
                        {
                            if (managerMemberMetadata.GetValue(this.WrappedOwner.Entity) != null)
                            {
                                this.WrappedOwner.SetCurrentValue(this.WrappedOwner.ObjectStateEntry, managerTypeMetadata.Member(olayerMemberName), -1, this.WrappedOwner.Entity, (object)null);
                            }
                            else if (flag2 && this.WrappedOwner.ObjectStateEntry.OriginalValues.GetValue(olayerMemberName) != null)
                            {
                                objectStateEntry.SetModifiedProperty(name);
                            }
                            flag1 = false;
                        }
                        else if (flag2)
                        {
                            objectStateEntry.SetModifiedProperty(name);
                        }
                    }
                    if (flag1)
                    {
                        if (objectStateEntry == null)
                        {
                            return;
                        }
                        EntityKey originalKey = this.CachedForeignKey;
                        if (originalKey == (EntityKey)null)
                        {
                            originalKey = ForeignKeyFactory.CreateKeyFromForeignKeyValues(objectStateEntry, (RelatedEnd)this);
                        }
                        if (!(originalKey != (EntityKey)null))
                        {
                            return;
                        }
                        this.SetCachedForeignKey(ForeignKeyFactory.CreateConceptualNullKey(originalKey), objectStateEntry);
                        objectStateManager.RememberEntryWithConceptualNull(objectStateEntry);
                    }
                    else
                    {
                        this.SetCachedForeignKey((EntityKey)null, objectStateEntry);
                    }
                }
                finally
                {
                    transactionManager.EndForeignKeyUpdate();
                }
            }
        }