private void UpdatePerItemLinkChangeGroupsByCheckingRelatedItemRecords( string sourceItemUri, LinkChangeGroup group, ILinkProvider linkProvider, bool createDeleteActionImplicitly) { var queryByItem = QueryByItem(sourceItemUri); var perItemExistingLinks = from link in queryByItem where link.RelationshipExistsOnServer select link; if (perItemExistingLinks.Count() == 0) { // no link existed before, all 'add' link actions should be pushed to the other side // AND we are going to record these links as existing on this side now AddLinks(group.Actions); } else { // check the delta link change actions // this list contains all the actions that do not need to push into the pipeline List <LinkChangeAction> actionThatEstablishExistingLinkRelationship = new List <LinkChangeAction>(); foreach (LinkChangeAction action in group.Actions) { if (action.Link.LinkType.GetsActionsFromLinkChangeHistory) { // For link types that can provide a history of link changes, we don't need to keep track of related artifact metadata // so continue to the next LinkChangeAction continue; } Debug.Assert(sourceItemUri.Equals(action.Link.SourceArtifact.Uri), "link of different action exists in the same group"); var linkQuery = from l in perItemExistingLinks where l.Relationship.Equals(action.Link.LinkType.ReferenceName) && l.RelatedArtifactId.Equals(action.Link.TargetArtifact.Uri) select l; if (action.ChangeActionId.Equals(WellKnownChangeActionId.Add)) { if (linkQuery.Count() > 0) { if (!linkQuery.First().OtherProperty.HasValue) { // link lock property is not available - required by backward-compability UpdateLink(action, true); } else { bool linkInStoreHasLock = (((WorkItemLinkStore.LinkLockStatus)linkQuery.First().OtherProperty.Value) == WorkItemLinkStore.LinkLockStatus.Locked); if (action.Link.IsLocked == linkInStoreHasLock) { // link already exist and lock-property matches - no need to push to the other side actionThatEstablishExistingLinkRelationship.Add(action); } else { UpdateLink(action, true); } } } else { // link does not exist, keep it and push through the pipeline // AND we are going to record these links as existing on this side now UpdateLink(action, true); } } else // delete { if (linkQuery.Count() > 0) { // link exists, so we will mark in our store that it no longer exists UpdateLink(action, false); } else { // link does not exist, no need to migrate this action actionThatEstablishExistingLinkRelationship.Add(action); } } } // make sure we generate "Delete Link" action for ones that exist in the our recorded link table but is not included // in delta link actions List <LinkChangeAction> deletionActions = new List <LinkChangeAction>(); if (createDeleteActionImplicitly) { foreach (var recordedExistingLink in perItemExistingLinks) { Debug.Assert(linkProvider.SupportedLinkTypes.ContainsKey(recordedExistingLink.Relationship), "linkProvider.SupportedLinkTypes.ContainsKey(recordedExistingLink.Relationship) returns false"); LinkType linkType = linkProvider.SupportedLinkTypes[recordedExistingLink.Relationship]; if (linkType.GetsActionsFromLinkChangeHistory) { // The link type is one that support link change history, so we ignore the contents of the // RelatedArtifactTable and rely on the link change history instead. continue; } bool recordedActionInGroup = false; foreach (LinkChangeAction action in group.Actions) { if (action.Link.LinkType.ReferenceName.Equals(recordedExistingLink.Relationship, StringComparison.OrdinalIgnoreCase) && action.Link.TargetArtifact.Uri.Equals(recordedExistingLink.RelatedArtifactId, StringComparison.OrdinalIgnoreCase)) { recordedActionInGroup = true; break; } } if (!recordedActionInGroup) { TraceManager.TraceInformation("Link '{0}'->'{1}' ({2}) appears to have been deleted - generating link deletion action to be migrated", recordedExistingLink.ItemId, recordedExistingLink.RelatedArtifactId, recordedExistingLink.Relationship); LinkChangeAction linkDeleteAction = linkType.CreateLinkDeletionAction( recordedExistingLink.ItemId, recordedExistingLink.RelatedArtifactId, recordedExistingLink.Relationship); if (null != linkDeleteAction) { deletionActions.Add(linkDeleteAction); } recordedExistingLink.RelationshipExistsOnServer = false; } } } if (actionThatEstablishExistingLinkRelationship.Count > 0) { foreach (LinkChangeAction actionToDelete in actionThatEstablishExistingLinkRelationship) { group.DeleteChangeAction(actionToDelete); } } if (deletionActions.Count > 0) { group.PrependActions(deletionActions); } } m_context.TrySaveChanges(); }