// ------- // Methods // ------- /// <summary> /// Loads the related entity or entities into the local related end using the supplied MergeOption. /// </summary> public override void Load(MergeOption mergeOption) { CheckOwnerNull(); // Validate that the Load is possible bool hasResults; ObjectQuery <TEntity> sourceQuery = ValidateLoad <TEntity>(mergeOption, "EntityReference", out hasResults); _suppressEvents = true; // we do not want any event during the bulk operation try { List <TEntity> refreshedValue = null; if (hasResults) { // Only issue a query if we know it can produce results (in the case of FK, there may not be any // results). refreshedValue = new List <TEntity>(GetResults <TEntity>(sourceQuery)); } if (null == refreshedValue || refreshedValue.Count == 0) { if (!((AssociationType)base.RelationMetadata).IsForeignKey && ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One) { //query returned zero related end; one related end was expected. throw EntityUtil.LessThanExpectedRelatedEntitiesFound(); } else if (mergeOption == MergeOption.OverwriteChanges || mergeOption == MergeOption.PreserveChanges) { // This entity is not related to anything in this AssociationSet and Role on the server. // If there is an existing _cachedValue, we may need to clear it out, based on the MergeOption EntityKey sourceKey = WrappedOwner.EntityKey; EntityUtil.CheckEntityKeyNull(sourceKey); ObjectStateManager.RemoveRelationships(ObjectContext, mergeOption, (AssociationSet)RelationshipSet, sourceKey, (AssociationEndMember)FromEndProperty); } // else this is NoTracking or AppendOnly, and no entity was retrieved by the Load, so there's nothing extra to do // Since we have no value and are not doing a merge, the last step is to set IsLoaded to true _isLoaded = true; } else if (refreshedValue.Count == 1) { Merge <TEntity>(refreshedValue, mergeOption, true /*setIsLoaded*/); } else { // More than 1 result, which is non-recoverable data inconsistency throw EntityUtil.MoreThanExpectedRelatedEntitiesFound(); } } finally { _suppressEvents = false; } // fire the AssociationChange with Refresh OnAssociationChanged(CollectionChangeAction.Refresh, null); }
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); } }