// <summary> // Call to ensure a target entities key is added into the state manager // properly // </summary> public IEntityWrapper HandleRelationshipSpan( IEntityWrapper wrappedEntity, EntityKey targetKey, AssociationEndMember targetMember) { if (null == wrappedEntity.Entity) { return(wrappedEntity); } DebugCheck.NotNull(targetMember); Debug.Assert( targetMember.RelationshipMultiplicity == RelationshipMultiplicity.One || targetMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne); var sourceKey = wrappedEntity.EntityKey; var sourceMember = MetadataHelper.GetOtherAssociationEnd(targetMember); CheckClearedEntryOnSpan(targetKey, wrappedEntity, sourceKey, targetMember); if (null != (object)targetKey) { EntitySet targetEntitySet; var associationSet = Context.MetadataWorkspace.MetadataOptimization.FindCSpaceAssociationSet( (AssociationType)targetMember.DeclaringType, targetMember.Name, targetKey.EntitySetName, targetKey.EntityContainerName, out targetEntitySet); Debug.Assert(associationSet != null, "associationSet should not be null"); var manager = Context.ObjectStateManager; EntityState newEntryState; // If there is an existing relationship entry, update it based on its current state and the MergeOption, otherwise add a new one var sourceRelationships = ObjectStateManager.GetRelationshipLookup(Context.ObjectStateManager, associationSet, sourceMember, sourceKey); if ( !ObjectStateManager.TryUpdateExistingRelationships( Context, MergeOption, associationSet, sourceMember, sourceRelationships, wrappedEntity, targetMember, targetKey, /*setIsLoaded*/ true, out newEntryState)) { // Try to find a state entry for the target key var targetEntry = manager.GetOrAddKeyEntry(targetKey, targetEntitySet); // For 1-1 relationships we have to take care of the relationships of targetEntity var needNewRelationship = true; switch (sourceMember.RelationshipMultiplicity) { case RelationshipMultiplicity.ZeroOrOne: case RelationshipMultiplicity.One: var targetRelationships = ObjectStateManager.GetRelationshipLookup(Context.ObjectStateManager, associationSet, targetMember, targetKey); // devnote: targetEntry can be a key entry (targetEntry.Entity == null), // but it that case this parameter won't be used in TryUpdateExistingRelationships needNewRelationship = !ObjectStateManager.TryUpdateExistingRelationships( Context, MergeOption, associationSet, targetMember, targetRelationships, targetEntry.WrappedEntity, sourceMember, sourceKey, setIsLoaded: true, newEntryState: out newEntryState); // It is possible that as part of removing existing relationships, the key entry was deleted // If that is the case, recreate the key entry if (targetEntry.State == EntityState.Detached) { targetEntry = manager.AddKeyEntry(targetKey, targetEntitySet); } break; case RelationshipMultiplicity.Many: // we always need a new relationship with Many-To-Many, if there was no exact match between these two entities, so do nothing break; default: Debug.Assert(false, "Unexpected sourceMember.RelationshipMultiplicity"); break; } if (needNewRelationship) { // If the target entry is a key entry, then we need to add a relation // between the source and target entries // If we are in a state where we just need to add a new Deleted relation, we // only need to do that and not touch the related ends // If the target entry is a full entity entry, then we need to add // the target entity to the source collection or reference if (targetEntry.IsKeyEntry || newEntryState == EntityState.Deleted) { // Add a relationship between the source entity and the target key entry var wrapper = new RelationshipWrapper( associationSet, sourceMember.Name, sourceKey, targetMember.Name, targetKey); manager.AddNewRelation(wrapper, newEntryState); } else { Debug.Assert(!targetEntry.IsRelationship, "how IsRelationship?"); if (targetEntry.State != EntityState.Deleted) { // The entry contains an entity, do collection or reference fixup // This will also try to create a new relationship entry or will revert the delete on an existing deleted relationship ObjectStateManager.AddEntityToCollectionOrReference( MergeOption, wrappedEntity, sourceMember, targetEntry.WrappedEntity, targetMember, setIsLoaded: true, relationshipAlreadyExists: false, inKeyEntryPromotion: false); } else { // if the target entry is deleted, then the materializer needs to create a deleted relationship // between the entity and the target entry so that if the entity is deleted, the update // pipeline can find the relationship (even though it is deleted) var wrapper = new RelationshipWrapper( associationSet, sourceMember.Name, sourceKey, targetMember.Name, targetKey); manager.AddNewRelation(wrapper, EntityState.Deleted); } } } } } else { RelatedEnd relatedEnd; if (TryGetRelatedEnd( wrappedEntity, (AssociationType)targetMember.DeclaringType, sourceMember.Name, targetMember.Name, out relatedEnd)) { SetIsLoadedForSpan(relatedEnd, false); } } // else there is nothing else for us to do, the relationship has been handled already return(wrappedEntity); }