private void SubmitChangesWithUpdateDoc( MigrationAction[] changeGroup, ConversionResult changeResult) { // NOTE / TODO: // Currently, work item revisions are submitted separately. To minimize server round-trips for // performance improvement, we may want to submit changes in batch. foreach (MigrationAction action in changeGroup) { try { XmlDocument updateDocument = CreateUpdateOperationDoc(action); if (updateDocument == null) { throw new InvalidOperationException("updateDocument is null"); } var updates = new XmlDocument[1] { updateDocument }; UpdateResult[] results = TfsBatchUpdateHelper.Submit(Core, WorkItemServer, updates); if (results.Length != updates.Length) { string msg = string.Format( TfsWITAdapterResources.Culture, TfsWITAdapterResources.ErrorWrongNumberOfUpdateResults, Core.ServerName, StoreName, updates.Length, results.Length); throw new Exception(msg); } for (int i = 0; i < results.Length; ++i) { UpdateResult rslt = results[i]; if (rslt.Exception != null) { throw rslt.Exception; } else { //TODO: Update watermark on pending update statements UpdateConversionHistory(action, rslt.Watermark, changeResult); } } } catch (Exception ex) { throw new MigrationException(changeResult, ex); } } }
/// <summary> /// Submits updates into the work item store. /// </summary> /// <param name="core">Target TFS</param> /// <param name="svc">Work item tracking service</param> /// <param name="updates">Updates to submit</param> /// <returns>Results</returns> public static UpdateResult[] Submit( TfsCore core, ITfsWorkItemServer svc, XmlDocument[] updates) { TraceManager.EnterMethod(core, svc, updates); TfsBatchUpdateHelper helper = new TfsBatchUpdateHelper(core, svc, updates); helper.Submit(0, updates.Length - 1); return(helper.m_results); }
protected bool SubmitNonWorkItemLinkChanges( LinkChangeGroup linkChanges, ServiceContainer serviceContainer, ConfigurationService configService, ITranslationService translationService, TfsLinkingProviderBase.LinkSubmissionPhase submissionPhase) { // group non-WorkItemLink changes by work item Id Dictionary <int, List <LinkChangeAction> > perWorkItemLinkChanges = RegroupLinkChangeActions(linkChanges); var orderedWorkitemId = new Dictionary <int, int>(); int index = 0; foreach (int workItemId in perWorkItemLinkChanges.Keys) { orderedWorkitemId.Add(index++, workItemId); } // batch-submit links of each work item var updateDocs = new List <XmlDocument>(perWorkItemLinkChanges.Count); foreach (var perWorkItemLinkChange in perWorkItemLinkChanges) { if (perWorkItemLinkChange.Value.Count == 0) { continue; } WorkItem workItem = WorkItemStore.GetWorkItem(perWorkItemLinkChange.Key); var tfsUpdateDocument = InitializeUpdateDocument(); tfsUpdateDocument.CreateWorkItemUpdateDoc(workItem); bool hasNonWorkItemLinkChanges = false; foreach (LinkChangeAction linkChangeAction in perWorkItemLinkChange.Value) { if (linkChangeAction.Status != LinkChangeAction.LinkChangeActionStatus.ReadyForMigration || linkChangeAction.IsConflicted || linkChangeAction.Link.LinkType is WorkItemLinkTypeBase) { continue; } if (!ProcessActionInCurrentSubmissionPhase(linkChangeAction, submissionPhase)) { continue; } hasNonWorkItemLinkChanges = true; var handler = linkChangeAction.Link.LinkType as ILinkHandler; Debug.Assert(null != handler); handler.UpdateTfs(tfsUpdateDocument, linkChangeAction); } if (hasNonWorkItemLinkChanges) { updateDocs.Add(tfsUpdateDocument.UpdateDocument); } } if (updateDocs.Count == 0) { return(true); } UpdateResult[] results = TfsBatchUpdateHelper.Submit(Core, WorkItemServer, updateDocs.ToArray()); if (results.Length != updateDocs.Count) { throw new SynchronizationEngineException("Wrong number of link update results."); } bool succeeded = true; for (int i = 0; i < results.Length; ++i) { UpdateResult rslt = results[i]; if (rslt.Exception != null && !rslt.Exception.Message.Contains("The specified link already exists")) { TraceManager.TraceError(rslt.Exception.ToString()); succeeded = false; // TODO // Try resolve conflict and push to backlog if resolution fails foreach (LinkChangeAction action in perWorkItemLinkChanges[orderedWorkitemId[i]]) { action.IsConflicted = true; } } else { foreach (LinkChangeAction action in perWorkItemLinkChanges[orderedWorkitemId[i]]) { if (ProcessActionInCurrentSubmissionPhase(action, submissionPhase)) { MarkLinkChangeActionCompleted(action); } } if (rslt.Exception == null) { UpdateLinkConversionHistory(configService, translationService, rslt, perWorkItemLinkChanges[orderedWorkitemId[i]]); } else if (rslt.Exception.Message.Contains("The specified link already exists")) { WorkItemLinkStore relatedArtifactsStore = new WorkItemLinkStore(configService.SourceId); relatedArtifactsStore.UpdateSyncedLinks(perWorkItemLinkChanges[orderedWorkitemId[i]]); } } } return(succeeded); }
private bool SubmitBatchedAddOrDeleteLinkChanges( List <XmlDocument> updateDocuments, Dictionary <int, LinkChangeAction> updateDocIndexToLinkChangeActionMap, ITranslationService translationService, ConfigurationService configService, ConflictManager conflictManager) { bool succeeded = true; UpdateResult[] results = TfsBatchUpdateHelper.Submit(Core, WorkItemServer, updateDocuments.ToArray()); if (results.Length != updateDocuments.Count) { throw new SynchronizationEngineException("Wrong number of link update results."); } // Collect list of successful LinkChangeActions (for LinkTypes with GetsActionsFromLinkChangeHistory true) to pass to SetServerLinkChangeIds() List <LinkChangeAction> actionsNeedingServerLinkIdSet = new List <LinkChangeAction>(); for (int i = 0; i < results.Length; ++i) { UpdateResult rslt = results[i]; if (rslt.Exception != null) { if (rslt.Exception.Message.Contains("The specified link already exists")) { // it is ok to eat this exception // mark the change action completed so it is not retried later LinkChangeAction action = updateDocIndexToLinkChangeActionMap[i]; action.Status = LinkChangeAction.LinkChangeActionStatus.Skipped; TraceManager.TraceInformation("Tried to add a link that already exists so skipping it: " + GetLinkChangeActionDescription(action)); System.Web.Services.Protocols.SoapException soapException = rslt.Exception as System.Web.Services.Protocols.SoapException; if (soapException != null) { TraceManager.TraceVerbose("SoapException.Detail.InnerXml for ignored exception: " + soapException.Detail.InnerXml); } } else if (updateDocIndexToLinkChangeActionMap[i].ChangeActionId == WellKnownChangeActionId.Delete && rslt.Exception.Message.Contains("This specified link does not exist")) { // it is ok to eat this exception and skip the action // mark the change action completed so it is not retried later LinkChangeAction action = updateDocIndexToLinkChangeActionMap[i]; action.Status = LinkChangeAction.LinkChangeActionStatus.Skipped; TraceManager.TraceInformation("Tried to delete a link that does not exist so skipping it: " + GetLinkChangeActionDescription(action)); System.Web.Services.Protocols.SoapException soapException = rslt.Exception as System.Web.Services.Protocols.SoapException; if (soapException != null) { TraceManager.TraceVerbose("SoapException.Detail.InnerXml for ignored exception: " + soapException.Detail.InnerXml); } } else if (rslt.Exception is System.Web.Services.Protocols.SoapException && null != rslt.Exception.Message && rslt.Exception.Message.StartsWith( TFSMulitpleParentLinkConflictType.SingleParentViolationMessage, StringComparison.OrdinalIgnoreCase)) { MigrationConflict conflict = TFSMulitpleParentLinkConflictType.CreateConflict( updateDocIndexToLinkChangeActionMap[i], rslt.Exception); List <MigrationAction> actions; var resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; succeeded = false; } } else if (rslt.Exception is System.Web.Services.Protocols.SoapException && null != rslt.Exception.Message && rslt.Exception.Message.StartsWith( TFSCyclicLinkConflictType.CircularityLinkHierarchyViolationMessage, StringComparison.OrdinalIgnoreCase)) { ILinkProvider linkProvider = ServiceContainer.GetService(typeof(ILinkProvider)) as ILinkProvider; Debug.Assert(null != linkProvider, "linkProvider is NULL"); LinkChangeAction conflictedAction = updateDocIndexToLinkChangeActionMap[i]; NonCyclicReferenceClosure linkRefClosure = linkProvider.CreateNonCyclicLinkReferenceClosure(conflictedAction.Link.LinkType, conflictedAction.Link.SourceArtifact); MigrationConflict conflict = TFSCyclicLinkConflictType.CreateConflict(conflictedAction, rslt.Exception, linkRefClosure); List <MigrationAction> actions; var resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; succeeded = false; } } else if (rslt.Exception is System.Web.Services.Protocols.SoapException && null != rslt.Exception.Message && rslt.Exception.Message.StartsWith( TFSModifyLockedWorkItemLinkConflictType.ModifyLockedWorkItemLinkViolationMessage, StringComparison.OrdinalIgnoreCase)) { MigrationConflict conflict = TFSModifyLockedWorkItemLinkConflictType.CreateConflict( updateDocIndexToLinkChangeActionMap[i], rslt.Exception); List <MigrationAction> actions; var resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; } // returning "not succeeded" so that the caller keeps this change group in "ReadyForMigration" status succeeded = false; } else if (rslt.Exception is System.Web.Services.Protocols.SoapException && null != rslt.Exception.Message && (rslt.Exception.Message.StartsWith(TFSLinkAccessViolationConflictType.LinkAccessViolationMessage1, StringComparison.OrdinalIgnoreCase) || rslt.Exception.Message.StartsWith(TFSLinkAccessViolationConflictType.LinkAccessViolationMessage2, StringComparison.OrdinalIgnoreCase))) { MigrationConflict conflict = TFSLinkAccessViolationConflictType.CreateConflict( updateDocIndexToLinkChangeActionMap[i], rslt.Exception); List <MigrationAction> actions; var resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; } // returning "not succeeded" so that the caller keeps this change group in "ReadyForMigration" status succeeded = false; } else { LinkChangeAction action = updateDocIndexToLinkChangeActionMap[i]; // TODO // Try resolve conflict and push to backlog if resolution fails action.IsConflicted = true; TraceManager.TraceError(String.Format(CultureInfo.InvariantCulture, "Exception processing {0}: {1}", GetLinkChangeActionDescription(action), rslt.Exception.ToString())); succeeded = false; } } else // rslt.Exception == null { LinkChangeAction successfulAction = updateDocIndexToLinkChangeActionMap[i]; MarkLinkChangeActionCompleted(successfulAction); TraceManager.TraceVerbose("Successful " + GetLinkChangeActionDescription(successfulAction)); List <LinkChangeAction> updatedActions = new List <LinkChangeAction>(); updatedActions.Add(successfulAction); if (successfulAction.Link.LinkType.GetsActionsFromLinkChangeHistory) { actionsNeedingServerLinkIdSet.Add(successfulAction); } UpdateLinkConversionHistory(configService, translationService, rslt, updatedActions); } } SetServerLinkChangeIds(actionsNeedingServerLinkIdSet); return(succeeded); }
/// <summary> /// This method is called when an attempt to add a link in the force sync case fails because a parent link already exists /// In this case we attempt to delete the existing parent link and retry the add link operation /// </summary> /// <param name="linkChangeAction">The LinkChangeAction object</param> /// <param name="updateDocumentThatFailed">The XmlDocument that was submitted to add the link that resulted in the error</param> /// <returns>True if successful at removing the existing parent and adding the new parent; false if not</returns> private bool DeleteExistingParentLinkToForceSyncAddLink(LinkChangeAction linkChangeAction, XmlDocument updateDocumentThatFailed) { // Get target work item ... string targetItem = TfsWorkItemHandler.IdFromUri(linkChangeAction.Link.TargetArtifact.Uri); WorkItem targetWorkItem = null; try { int targetWorkItemId = int.Parse(targetItem); targetWorkItem = base.WorkItemStore.GetWorkItem(targetWorkItemId); } catch (FormatException) { } catch (DeniedOrNotExistException) { } if (targetWorkItem != null) { // Delete its parent link ... string linkType = linkChangeAction.Link.LinkType.ReferenceName; WorkItemLink linkToRemove = null; foreach (WorkItemLink link in targetWorkItem.WorkItemLinks) { if (!link.LinkTypeEnd.IsForwardLink && string.Equals(link.LinkTypeEnd.LinkType.ReferenceName, linkType)) { linkToRemove = link; break; } } if (linkToRemove != null) { try { // Create a delete LinkChangeAction by copying from the add LinkChangeAction and // changing the SourceId which is the parent in the add parent link case. LinkChangeAction deletelinkChangeAction = new LinkChangeAction(WellKnownChangeActionId.Delete, linkChangeAction.Link, linkChangeAction.Status, false); deletelinkChangeAction.Link.SourceArtifact.Uri = TfsWorkItemHandler.UriFromId(linkToRemove.TargetId.ToString()); TfsUpdateDocument tfsUpdateDocument = InitializeUpdateDocument(); var handler = deletelinkChangeAction.Link.LinkType as ILinkHandler; Debug.Assert(null != handler); handler.UpdateTfs(tfsUpdateDocument, deletelinkChangeAction); XmlDocument[] xmlUpdateDocuments = new XmlDocument[] { tfsUpdateDocument.UpdateDocument }; UpdateResult[] deleteExistingParentResults = TfsBatchUpdateHelper.Submit(Core, WorkItemServer, xmlUpdateDocuments); Debug.Assert(deleteExistingParentResults.Length == 1); if (deleteExistingParentResults[0].Exception != null) { throw deleteExistingParentResults[0].Exception; } } catch (Exception ex) { TraceManager.TraceWarning(String.Format(CultureInfo.InvariantCulture, "An exception occurred while trying to remove the parent link to force sync a link change to target work item {0}: {1}", targetWorkItem.Id, ex.ToString())); targetWorkItem = null; } if (targetWorkItem != null) { // Retry the operation XmlDocument[] retryDocuments = new XmlDocument[1] { updateDocumentThatFailed }; UpdateResult[] retryResults = TfsBatchUpdateHelper.Submit(Core, WorkItemServer, retryDocuments); if (retryResults.Length != retryDocuments.Length) { throw new SynchronizationEngineException("Wrong number of link update results."); } if (retryResults[0].Exception == null) { // Successfully retried after removing existing parent; don't create conflict return(true); } else { TraceManager.TraceWarning(String.Format(CultureInfo.InvariantCulture, "An exception occurred while trying to force sync a link change to target work item {0}: {1}", targetWorkItem.Id, retryResults[0].Exception.ToString())); } } } } return(false); }
private bool SubmitBatchedAddOrDeleteLinkChanges( List <XmlDocument> updateDocuments, Dictionary <int, LinkChangeAction> updateDocIndexToLinkChangeActionMap, ITranslationService translationService, ConfigurationService configService, ConflictManager conflictManager) { bool succeeded = true; UpdateResult[] results = TfsBatchUpdateHelper.Submit(Core, WorkItemServer, updateDocuments.ToArray()); if (results.Length != updateDocuments.Count) { throw new SynchronizationEngineException("Wrong number of link update results."); } // Collect list of successful LinkChangeActions (for LinkTypes with GetsActionsFromLinkChangeHistory true) to pass to SetServerLinkChangeIds() List <LinkChangeAction> actionsNeedingServerLinkIdSet = new List <LinkChangeAction>(); for (int i = 0; i < results.Length; ++i) { if (results[i] == null) { continue; } UpdateResult rslt = results[i]; if (rslt.Exception != null) { MigrationConflict conflict = null; ConflictResolutionResult resolutionRslt = null; List <MigrationAction> actions; bool createWitGeneralConflict = false; System.Web.Services.Protocols.SoapException soapException = rslt.Exception as System.Web.Services.Protocols.SoapException; if (soapException != null) { int?tfsErrorNumber = GetTfsErrorNumberFromSoapException(soapException); if (tfsErrorNumber.HasValue) { LinkChangeAction linkChangeAction = updateDocIndexToLinkChangeActionMap[i]; switch (tfsErrorNumber) { case TfsConstants.TfsError_AddLink_LinkExists: case TfsConstants.TfsError_DeleteLink_LinkNotFound: // it is ok to eat these exception and skip the action // mark the change action completed so it is not retried later linkChangeAction.Status = LinkChangeAction.LinkChangeActionStatus.Skipped; if (tfsErrorNumber == TfsConstants.TfsError_AddLink_LinkExists) { TraceManager.TraceInformation("Tried to add a link that already exists so skipping it: " + GetLinkChangeActionDescription(linkChangeAction)); } else { TraceManager.TraceInformation("Tried to delete a link that does not exist so skipping it: " + GetLinkChangeActionDescription(linkChangeAction)); } if (soapException.Detail != null) { TraceManager.TraceVerbose("SoapException.Detail.InnerXml for ignored exception: " + soapException.Detail.InnerXml); } break; case TfsConstants.TfsError_AddLink_TooManyParents: if (linkChangeAction.Group.IsForcedSync) { if (DeleteExistingParentLinkToForceSyncAddLink(linkChangeAction, updateDocuments[i])) { break; } } conflict = TFSMulitpleParentLinkConflictType.CreateConflict( updateDocIndexToLinkChangeActionMap[i], rslt.Exception); resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; succeeded = false; } break; case TfsConstants.TfsError_AddLink_Circular: case TfsConstants.TfsError_AddLink_ChildIsAncestor: ILinkProvider linkProvider = ServiceContainer.GetService(typeof(ILinkProvider)) as ILinkProvider; Debug.Assert(null != linkProvider, "linkProvider is NULL"); LinkChangeAction conflictedAction = updateDocIndexToLinkChangeActionMap[i]; NonCyclicReferenceClosure linkRefClosure = linkProvider.CreateNonCyclicLinkReferenceClosure(conflictedAction.Link.LinkType, conflictedAction.Link.SourceArtifact); conflict = TFSCyclicLinkConflictType.CreateConflict(conflictedAction, rslt.Exception, linkRefClosure); resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; succeeded = false; } break; case TfsConstants.TfsError_LinkAuthorizationFailedLinkLocked: conflict = TFSModifyLockedWorkItemLinkConflictType.CreateConflict( updateDocIndexToLinkChangeActionMap[i], rslt.Exception); resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; } // returning "not succeeded" so that the caller keeps this change group in "ReadyForMigration" status succeeded = false; break; case TfsConstants.TfsError_LinkAuthorizationFailed: case TfsConstants.TfsError_LinkAuthorizationFailedNotServiceAccount: conflict = TFSLinkAccessViolationConflictType.CreateConflict( updateDocIndexToLinkChangeActionMap[i], rslt.Exception); resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; } // returning "not succeeded" so that the caller keeps this change group in "ReadyForMigration" status succeeded = false; break; default: // TFS error doesn't match any that we explicitly handle TraceManager.TraceError("SubmitBatchedAddOrDeleteLinkChanges:TFS error number in SoapException not explictly handled: {0}", tfsErrorNumber); createWitGeneralConflict = true; break; } } else { TraceManager.TraceError("SubmitBatchedAddOrDeleteLinkChanges: Unable to get TFS error number from SoapException: {0}", soapException.ToString()); createWitGeneralConflict = true; } } else // Exception is not SoapException { TraceManager.TraceError("SubmitBatchedAddOrDeleteLinkChanges: Exception returned is not SoapException: {0}", rslt.Exception.ToString()); createWitGeneralConflict = true; } if (createWitGeneralConflict) { conflict = WitGeneralConflictType.CreateConflict(rslt.Exception); resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; succeeded = false; } } } else // rslt.Exception == null { LinkChangeAction successfulAction = updateDocIndexToLinkChangeActionMap[i]; MarkLinkChangeActionCompleted(successfulAction); TraceManager.TraceVerbose("Successful " + GetLinkChangeActionDescription(successfulAction)); List <LinkChangeAction> updatedActions = new List <LinkChangeAction>(); updatedActions.Add(successfulAction); if (successfulAction.Link.LinkType.GetsActionsFromLinkChangeHistory) { actionsNeedingServerLinkIdSet.Add(successfulAction); } UpdateLinkConversionHistory(configService, translationService, rslt, updatedActions); } } SetServerLinkChangeIds(actionsNeedingServerLinkIdSet); return(succeeded); }
private bool SubmitBatchedAddOrDeleteLinkChanges( List <XmlDocument> updateDocuments, Dictionary <int, LinkChangeAction> updateDocIndexToLinkChangeActionMap, ITranslationService translationService, ConfigurationService configService, ConflictManager conflictManager) { bool succeeded = true; UpdateResult[] results = TfsBatchUpdateHelper.Submit(Core, WorkItemServer, updateDocuments.ToArray()); if (results.Length != updateDocuments.Count) { throw new SynchronizationEngineException("Wrong number of link update results."); } for (int i = 0; i < results.Length; ++i) { UpdateResult rslt = results[i]; if (rslt.Exception != null) { if (rslt.Exception.Message.Contains("The specified link already exists")) { // it is ok to eat this exception } else if (rslt.Exception is System.Web.Services.Protocols.SoapException && null != rslt.Exception.Message && rslt.Exception.Message.StartsWith( TFSMulitpleParentLinkConflictType.SingleParentViolationMessage, StringComparison.OrdinalIgnoreCase)) { MigrationConflict conflict = TFSMulitpleParentLinkConflictType.CreateConflict( updateDocIndexToLinkChangeActionMap[i], rslt.Exception); List <MigrationAction> actions; var resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; succeeded = false; } } else if (rslt.Exception is System.Web.Services.Protocols.SoapException && null != rslt.Exception.Message && rslt.Exception.Message.StartsWith( TFSCyclicLinkConflictType.CircularityLinkHierarchyViolationMessage, StringComparison.OrdinalIgnoreCase)) { ILinkProvider linkProvider = ServiceContainer.GetService(typeof(ILinkProvider)) as ILinkProvider; Debug.Assert(null != linkProvider, "linkProvider is NULL"); LinkChangeAction conflictedAction = updateDocIndexToLinkChangeActionMap[i]; NonCyclicReferenceClosure linkRefClosure = linkProvider.CreateNonCyclicLinkReferenceClosure(conflictedAction.Link.LinkType, conflictedAction.Link.SourceArtifact); MigrationConflict conflict = TFSCyclicLinkConflictType.CreateConflict(conflictedAction, rslt.Exception, linkRefClosure); List <MigrationAction> actions; var resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; succeeded = false; } } else if (rslt.Exception is System.Web.Services.Protocols.SoapException && null != rslt.Exception.Message && rslt.Exception.Message.StartsWith( TFSModifyLockedWorkItemLinkConflictType.ModifyLockedWorkItemLinkViolationMessage, StringComparison.OrdinalIgnoreCase)) { MigrationConflict conflict = TFSModifyLockedWorkItemLinkConflictType.CreateConflict( updateDocIndexToLinkChangeActionMap[i], rslt.Exception); List <MigrationAction> actions; var resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; } // returning "not succeeded" so that the caller keeps this change group in "ReadyForMigration" status succeeded = false; } else if (rslt.Exception is System.Web.Services.Protocols.SoapException && null != rslt.Exception.Message && (rslt.Exception.Message.StartsWith(TFSLinkAccessViolationConflictType.LinkAccessViolationMessage1, StringComparison.OrdinalIgnoreCase) || rslt.Exception.Message.StartsWith(TFSLinkAccessViolationConflictType.LinkAccessViolationMessage2, StringComparison.OrdinalIgnoreCase))) { MigrationConflict conflict = TFSLinkAccessViolationConflictType.CreateConflict( updateDocIndexToLinkChangeActionMap[i], rslt.Exception); List <MigrationAction> actions; var resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; } // returning "not succeeded" so that the caller keeps this change group in "ReadyForMigration" status succeeded = false; } else { TraceManager.TraceError(rslt.Exception.ToString()); succeeded = false; // TODO // Try resolve conflict and push to backlog if resolution fails updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; } } else { foreach (LinkChangeAction action in updateDocIndexToLinkChangeActionMap.Values) { MarkLinkChangeActionCompleted(action); } List <LinkChangeAction> updatedActions = new List <LinkChangeAction>(updateDocIndexToLinkChangeActionMap.Values); if (rslt.Exception == null) { UpdateLinkConversionHistory(configService, translationService, rslt, updatedActions); } else if (rslt.Exception.Message.Contains("The specified link already exists")) { WorkItemLinkStore relatedArtifactsStore = new WorkItemLinkStore(configService.SourceId); relatedArtifactsStore.UpdateSyncedLinks(updatedActions); } } } return(succeeded); }