internal void UpdateForeignKeyValues(IEntityWrapper dependentEntity, EntityKey principalKey) { ReferentialConstraint referentialConstraint = ((AssociationType)this.RelationMetadata).ReferentialConstraints[0]; ObjectStateManager objectStateManager = this.ObjectContext.ObjectStateManager; objectStateManager.TransactionManager.BeginForeignKeyUpdate(this); try { EntitySet entitySet = ((AssociationSet)this.RelationshipSet).AssociationSetEnds[this.FromEndMember.Name].EntitySet; StateManagerTypeMetadata managerTypeMetadata = objectStateManager.GetOrAddStateManagerTypeMetadata(dependentEntity.IdentityType, entitySet); for (int index = 0; index < referentialConstraint.FromProperties.Count; ++index) { object valueByName = principalKey.FindValueByName(referentialConstraint.FromProperties[index].Name); int olayerMemberName = managerTypeMetadata.GetOrdinalforOLayerMemberName(referentialConstraint.ToProperties[index].Name); object x = managerTypeMetadata.Member(olayerMemberName).GetValue(dependentEntity.Entity); if (!ByValueEqualityComparer.Default.Equals(x, valueByName)) { dependentEntity.SetCurrentValue(dependentEntity.ObjectStateEntry, managerTypeMetadata.Member(olayerMemberName), -1, dependentEntity.Entity, valueByName); } } this.SetCachedForeignKey(principalKey, dependentEntity.ObjectStateEntry); if (this.WrappedOwner.ObjectStateEntry == null) { return; } objectStateManager.ForgetEntryWithConceptualNull(this.WrappedOwner.ObjectStateEntry, false); } finally { objectStateManager.TransactionManager.EndForeignKeyUpdate(); } }
/// <summary> /// 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. /// </summary> /// <param name="dependentEntity">The entity into which foreign key values will be written</param> /// <param name="principalEntity">The key from which key values will be obtained</param> 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(); } }
/// <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(); } } } } }
/// <summary> /// 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. /// </summary> /// <param name="dependentEntity">The entity into which foreign key values will be written</param> /// <param name="principalEntity">The entity from which key values will be obtained</param> /// <param name="changedFKs">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</param> /// <param name="forceChange">If true, then the property setter is called even if FK values already match, /// which causes the FK properties to be marked as modified.</param> internal void UpdateForeignKeyValues(IEntityWrapper dependentEntity, IEntityWrapper principalEntity, Dictionary <int, object> 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(); } }
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(); } } }
internal void UpdateForeignKeyValues( IEntityWrapper dependentEntity, IEntityWrapper principalEntity, Dictionary <int, object> changedFKs, bool forceChange) { ReferentialConstraint referentialConstraint = ((AssociationType)this.RelationMetadata).ReferentialConstraints[0]; bool flag = (object)this.WrappedOwner.EntityKey != null && !this.WrappedOwner.EntityKey.IsTemporary && this.IsDependentEndOfReferentialConstraint(true); ObjectStateManager objectStateManager = this.ObjectContext.ObjectStateManager; objectStateManager.TransactionManager.BeginForeignKeyUpdate(this); try { EntitySet entitySet1 = ((AssociationSet)this.RelationshipSet).AssociationSetEnds[this.ToEndMember.Name].EntitySet; StateManagerTypeMetadata managerTypeMetadata1 = objectStateManager.GetOrAddStateManagerTypeMetadata(principalEntity.IdentityType, entitySet1); EntitySet entitySet2 = ((AssociationSet)this.RelationshipSet).AssociationSetEnds[this.FromEndMember.Name].EntitySet; StateManagerTypeMetadata managerTypeMetadata2 = objectStateManager.GetOrAddStateManagerTypeMetadata(dependentEntity.IdentityType, entitySet2); ReadOnlyMetadataCollection <EdmProperty> fromProperties = referentialConstraint.FromProperties; int count = fromProperties.Count; string[] array = (string[])null; object[] compositeKeyValues = (object[])null; if (count > 1) { array = entitySet1.ElementType.KeyMemberNames; compositeKeyValues = new object[count]; } for (int index1 = 0; index1 < count; ++index1) { int olayerMemberName1 = managerTypeMetadata1.GetOrdinalforOLayerMemberName(fromProperties[index1].Name); object obj = managerTypeMetadata1.Member(olayerMemberName1).GetValue(principalEntity.Entity); int olayerMemberName2 = managerTypeMetadata2.GetOrdinalforOLayerMemberName(referentialConstraint.ToProperties[index1].Name); bool changingForeignKeyValue = !ByValueEqualityComparer.Default.Equals(managerTypeMetadata2.Member(olayerMemberName2).GetValue(dependentEntity.Entity), obj); if (forceChange || changingForeignKeyValue) { if (flag) { this.ValidateSettingRIConstraints(principalEntity, obj == null, changingForeignKeyValue); } if (changedFKs != null) { object x; if (changedFKs.TryGetValue(olayerMemberName2, out x)) { if (!ByValueEqualityComparer.Default.Equals(x, obj)) { throw new InvalidOperationException(Strings.Update_ReferentialConstraintIntegrityViolation); } } else { changedFKs[olayerMemberName2] = obj; } } dependentEntity.SetCurrentValue(dependentEntity.ObjectStateEntry, managerTypeMetadata2.Member(olayerMemberName2), -1, dependentEntity.Entity, obj); } if (count > 1) { int index2 = Array.IndexOf <string>(array, fromProperties[index1].Name); compositeKeyValues[index2] = obj; } else { this.SetCachedForeignKey(obj == null ? (EntityKey)null : new EntityKey((EntitySetBase)entitySet1, obj), dependentEntity.ObjectStateEntry); } } if (count > 1) { this.SetCachedForeignKey(((IEnumerable <object>)compositeKeyValues).Any <object>((Func <object, bool>)(v => v == null)) ? (EntityKey)null : new EntityKey((EntitySetBase)entitySet1, compositeKeyValues), dependentEntity.ObjectStateEntry); } if (this.WrappedOwner.ObjectStateEntry == null) { return; } objectStateManager.ForgetEntryWithConceptualNull(this.WrappedOwner.ObjectStateEntry, false); } finally { objectStateManager.TransactionManager.EndForeignKeyUpdate(); } }