private void IncludeEntityKey(bool doAttach) { ObjectStateManager objectStateManager = this.ObjectContext.ObjectStateManager; bool flag1 = false; bool flag2 = false; EntityEntry entityEntry = objectStateManager.FindEntityEntry(this.DetachedEntityKey); if (entityEntry == null) { flag2 = true; flag1 = true; } else if (entityEntry.IsKeyEntry) { if (this.FromEndMember.RelationshipMultiplicity != RelationshipMultiplicity.Many) { foreach (RelationshipEntry relationshipEntry in this.ObjectContext.ObjectStateManager.FindRelationshipsByKey(this.DetachedEntityKey)) { if (relationshipEntry.IsSameAssociationSetAndRole((AssociationSet)this.RelationshipSet, (AssociationEndMember)this.ToEndMember, this.DetachedEntityKey) && relationshipEntry.State != EntityState.Deleted) { throw new InvalidOperationException(Strings.ObjectStateManager_EntityConflictsWithKeyEntry); } } } flag1 = true; } else { IEntityWrapper wrappedEntity = entityEntry.WrappedEntity; if (entityEntry.State == EntityState.Deleted) { throw new InvalidOperationException(Strings.RelatedEnd_UnableToAddRelationshipWithDeletedEntity); } RelatedEnd relatedEndInternal = wrappedEntity.RelationshipManager.GetRelatedEndInternal(this.RelationshipName, this.RelationshipNavigation.From); if (this.FromEndMember.RelationshipMultiplicity != RelationshipMultiplicity.Many && !relatedEndInternal.IsEmpty()) { throw new InvalidOperationException(Strings.ObjectStateManager_EntityConflictsWithKeyEntry); } this.Add(wrappedEntity, true, doAttach, false, true, true); objectStateManager.TransactionManager.PopulatedEntityReferences.Add((EntityReference)this); } if (!flag1 || this.IsForeignKey) { return; } if (flag2) { EntitySet entitySet = this.DetachedEntityKey.GetEntitySet(this.ObjectContext.MetadataWorkspace); objectStateManager.AddKeyEntry(this.DetachedEntityKey, entitySet); } EntityKey entityKey = this.WrappedOwner.EntityKey; if ((object)entityKey == null) { throw Error.EntityKey_UnexpectedNull(); } RelationshipWrapper wrapper = new RelationshipWrapper((AssociationSet)this.RelationshipSet, this.RelationshipNavigation.From, entityKey, this.RelationshipNavigation.To, this.DetachedEntityKey); objectStateManager.AddNewRelation(wrapper, doAttach ? EntityState.Unchanged : EntityState.Added); }
internal virtual IEntityWrapper WrapEntityUsingStateManagerGettingEntry( object entity, ObjectStateManager stateManager, out EntityEntry existingEntry) { IEntityWrapper wrapper = (IEntityWrapper)null; existingEntry = (EntityEntry)null; if (entity == null) { return(NullEntityWrapper.NullWrapper); } if (stateManager != null) { existingEntry = stateManager.FindEntityEntry(entity); if (existingEntry != null) { return(existingEntry.WrappedEntity); } if (stateManager.TransactionManager.TrackProcessedEntities && stateManager.TransactionManager.WrappedEntities.TryGetValue(entity, out wrapper)) { return(wrapper); } } IEntityWithRelationships withRelationships = entity as IEntityWithRelationships; if (withRelationships != null) { RelationshipManager relationshipManager = withRelationships.RelationshipManager; if (relationshipManager == null) { throw new InvalidOperationException(Strings.RelationshipManager_UnexpectedNull); } IEntityWrapper wrappedOwner = relationshipManager.WrappedOwner; if (!object.ReferenceEquals(wrappedOwner.Entity, entity)) { throw new InvalidOperationException(Strings.RelationshipManager_InvalidRelationshipManagerOwner); } return(wrappedOwner); } EntityProxyFactory.TryGetProxyWrapper(entity, out wrapper); if (wrapper == null) { IEntityWithKey entityWithKey = entity as IEntityWithKey; wrapper = EntityWrapperFactory.CreateNewWrapper(entity, entityWithKey == null ? (EntityKey)null : entityWithKey.EntityKey); } if (stateManager != null && stateManager.TransactionManager.TrackProcessedEntities) { stateManager.TransactionManager.WrappedEntities.Add(entity, wrapper); } return(wrapper); }
private void IncludeEntityKey(bool doAttach) { ObjectStateManager manager = this.ObjectContext.ObjectStateManager; bool addNewRelationship = false; bool addKeyEntry = false; EntityEntry existingEntry = manager.FindEntityEntry(DetachedEntityKey); if (existingEntry == null) { // add new key entry and create a relationship with it addKeyEntry = true; addNewRelationship = true; } else { if (existingEntry.IsKeyEntry) { // We have an existing key entry, so just need to add a relationship with it // We know the target end of this relationship is 1..1 or 0..1 since it is a reference, so if the source end is also not Many, we have a 1-to-1 if (FromEndProperty.RelationshipMultiplicity != RelationshipMultiplicity.Many) { // before we add a new relationship to this key entry, make sure it's not already related to something else // We have to explicitly do this here because there are no other checks to make sure a key entry in a 1-to-1 doesn't end up in two of the same relationship foreach (RelationshipEntry relationshipEntry in this.ObjectContext.ObjectStateManager.FindRelationshipsByKey(DetachedEntityKey)) { // only care about relationships in the same AssociationSet and where the key is playing the same role that it plays in this EntityReference if (relationshipEntry.IsSameAssociationSetAndRole((AssociationSet)RelationshipSet, (AssociationEndMember)ToEndMember, DetachedEntityKey) && relationshipEntry.State != EntityState.Deleted) { throw EntityUtil.EntityConflictsWithKeyEntry(); } } } addNewRelationship = true; } else { IEntityWrapper wrappedTarget = existingEntry.WrappedEntity; // Verify that the target entity is in a valid state for adding a relationship if (existingEntry.State == EntityState.Deleted) { throw EntityUtil.UnableToAddRelationshipWithDeletedEntity(); } // We know the target end of this relationship is 1..1 or 0..1 since it is a reference, so if the source end is also not Many, we have a 1-to-1 RelatedEnd relatedEnd = wrappedTarget.RelationshipManager.GetRelatedEndInternal(RelationshipName, RelationshipNavigation.From); if (FromEndProperty.RelationshipMultiplicity != RelationshipMultiplicity.Many && !relatedEnd.IsEmpty()) { // Make sure the target entity is not already related to something else. // devnote: The call to Add below does *not* do this check for the fixup case, so if it's not done here, no failure will occur // and existing relationships may be deleted unexpectedly. RelatedEnd.Include should not remove existing relationships, only add new ones. throw EntityUtil.EntityConflictsWithKeyEntry(); } // We have an existing entity with the same key, just hook up the related ends this.Add(wrappedTarget, applyConstraints: true, addRelationshipAsUnchanged: doAttach, relationshipAlreadyExists: false, allowModifyingOtherEndOfRelationship: true, forceForeignKeyChanges: true); // add to the list of promoted key references so we can cleanup if a failure occurs later manager.TransactionManager.PopulatedEntityReferences.Add(this); } } // For FKs, don't create a key entry and don't create a relationship if (addNewRelationship && !IsForeignKey) { // devnote: If we add any validation here, it needs to go here before adding the key entry, // otherwise we have to clean up that entry if the validation fails if (addKeyEntry) { EntitySet targetEntitySet = DetachedEntityKey.GetEntitySet(this.ObjectContext.MetadataWorkspace); manager.AddKeyEntry(DetachedEntityKey, targetEntitySet); } EntityKey ownerKey = WrappedOwner.EntityKey; EntityUtil.CheckEntityKeyNull(ownerKey); RelationshipWrapper wrapper = new RelationshipWrapper((AssociationSet)RelationshipSet, RelationshipNavigation.From, ownerKey, RelationshipNavigation.To, DetachedEntityKey); manager.AddNewRelation(wrapper, doAttach ? EntityState.Unchanged : EntityState.Added); } }
/// <summary> /// Wraps an entity and returns a new wrapper, or returns an existing wrapper if one /// already exists in the ObjectStateManager or in a RelationshipManager associated with /// the entity. /// </summary> /// <param name="entity">The entity to wrap</param> /// <param name="context">The state manager in which the entity may exist, or null</param> /// <param name="existingEntry">The existing state entry for the given entity if one exists, otherwise null</param> /// <returns>A new or existing wrapper</returns> internal static IEntityWrapper WrapEntityUsingStateManagerGettingEntry(object entity, ObjectStateManager stateManager, out EntityEntry existingEntry) { Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity."); IEntityWrapper wrapper = null; existingEntry = null; if (entity == null) { return(NullEntityWrapper.NullWrapper); } // First attempt to find an existing wrapper in the ObjectStateMager. if (stateManager != null) { existingEntry = stateManager.FindEntityEntry(entity); if (existingEntry != null) { return(existingEntry.WrappedEntity); } if (stateManager.TransactionManager.TrackProcessedEntities) { if (stateManager.TransactionManager.WrappedEntities.TryGetValue(entity, out wrapper)) { return(wrapper); } } } // If no entity was found in the OSM, then check if one exists on an associated // RelationshipManager. This only works where the entity implements IEntityWithRelationshops. IEntityWithRelationships entityWithRelationships = entity as IEntityWithRelationships; if (entityWithRelationships != null) { RelationshipManager relManager = entityWithRelationships.RelationshipManager; if (relManager == null) { throw EntityUtil.UnexpectedNullRelationshipManager(); } IEntityWrapper wrappedEntity = relManager.WrappedOwner; if (!Object.ReferenceEquals(wrappedEntity.Entity, entity)) { // This means that the owner of the RelationshipManager must have been set // incorrectly in the call to RelationshipManager.Create(). throw EntityUtil.InvalidRelationshipManagerOwner(); } return(wrappedEntity); } else { // Finally look to see if the instance is a proxy and get the wrapper from the proxy EntityProxyFactory.TryGetProxyWrapper(entity, out wrapper); } // If we could not find an existing wrapper, then go create a new one if (wrapper == null) { IEntityWithKey withKey = entity as IEntityWithKey; wrapper = CreateNewWrapper(entity, withKey == null ? null : withKey.EntityKey); } if (stateManager != null && stateManager.TransactionManager.TrackProcessedEntities) { stateManager.TransactionManager.WrappedEntities.Add(entity, wrapper); } return(wrapper); }
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) { if (AttemptToNullFKsOnRefOrKeySetToNull()) { DetachedEntityKey = null; } else { 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(ObjectContext.MetadataWorkspace, 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 void SetEntityKey(EntityKey value, bool forceFixup) { if (value != (EntityKey)null && value == this.EntityKey && (this.ReferenceValue.Entity != null || this.ReferenceValue.Entity == null && !forceFixup)) { return; } if (this.ObjectContext != null && !this.UsingNoTracking) { if (value != (EntityKey)null && !RelatedEnd.IsValidEntityKeyType(value)) { throw new ArgumentException(Strings.EntityReference_CannotSetSpecialKeys, nameof(value)); } if (value == (EntityKey)null) { if (this.AttemptToNullFKsOnRefOrKeySetToNull()) { this.DetachedEntityKey = (EntityKey)null; } else { this.ReferenceValue = NullEntityWrapper.NullWrapper; } } else { EntitySet entitySet = value.GetEntitySet(this.ObjectContext.MetadataWorkspace); this.CheckRelationEntitySet(entitySet); value.ValidateEntityKey(this.ObjectContext.MetadataWorkspace, entitySet, true, nameof(value)); ObjectStateManager objectStateManager = this.ObjectContext.ObjectStateManager; bool flag1 = false; bool flag2 = false; EntityEntry entityEntry = objectStateManager.FindEntityEntry(value); if (entityEntry != null) { if (!entityEntry.IsKeyEntry) { this.ReferenceValue = entityEntry.WrappedEntity; } else { flag1 = true; } } else { flag2 = !this.IsForeignKey; flag1 = true; } if (!flag1) { return; } EntityKey key0 = this.ValidateOwnerWithRIConstraints(entityEntry == null ? (IEntityWrapper)null : entityEntry.WrappedEntity, value, true); this.ValidateStateForAdd(this.WrappedOwner); if (flag2) { objectStateManager.AddKeyEntry(value, entitySet); } objectStateManager.TransactionManager.EntityBeingReparented = this.WrappedOwner.Entity; try { this.ClearCollectionOrRef((IEntityWrapper)null, (RelationshipNavigation)null, false); } finally { objectStateManager.TransactionManager.EntityBeingReparented = (object)null; } if (this.IsForeignKey) { this.DetachedEntityKey = value; if (!this.IsDependentEndOfReferentialConstraint(false)) { return; } this.UpdateForeignKeyValues(this.WrappedOwner, value); } else { RelationshipWrapper wrapper = new RelationshipWrapper((AssociationSet)this.RelationshipSet, this.RelationshipNavigation.From, key0, this.RelationshipNavigation.To, value); EntityState desiredState = EntityState.Added; if (!key0.IsTemporary && this.IsDependentEndOfReferentialConstraint(false)) { desiredState = EntityState.Unchanged; } objectStateManager.AddNewRelation(wrapper, desiredState); } } } else { this.DetachedEntityKey = value; } }