예제 #1
0
        // <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);
        }