2009-11-17 139 views
1

我正在開發一個Windows應用程序,它使用2010 Beta 2 API執行一些常見的TFS任務(如創建新的團隊項目,新工作項目,選擇性構建等)。工作項目狀態的轉換工作流程

在編輯現有的工作項目的過程中,我應該能夠根據WI(模擬-ING的Visual Studio)的狀態變化自動設定「的原因」字段的值。 (例如) - 當我編輯一個bug時,當狀態從活動變爲已解決時,默認原因是'固定',類似地,當狀態從活動變爲關閉時,默認原因='延遲'。 (如工作項類型定義xml文件中所定義的。)該轉換很容易在窗體上的簡單事件處理程序中捕獲和實現,因爲當首次編輯Bug時,初始狀態將爲「活動」。

我想知道如何實現剩下的轉換,如解決關閉(原因=固定),解決到活動(原因=測試失敗/不固定)或關閉到活動(原因=重新激活/迴歸)。

我知道有一個叫WorkItem.GetNextState(current_state,動作)方法,但是這並沒有幫助,因爲它需要一個具體的行動。

我迄今所做如下圖所示:

void cmbBugState_SelectedIndexChanged(object sender, EventArgs e) 
    { 
     //private enum bugWorkFlows{"Fixed","Deferred","Duplicate","As Designed","Cannot Reproduce","Obsolete","Test Failed","Not Fixed","Reactivated","Regression"} 
     string[] activeToResolvedReasons = { "Fixed", "Deferred", "Duplicate", "As Designed", "Cannot Reproduce", "Obsolete" }; 
     string[] resolvedToActiveReasons = { "Test Failed", "Not fixed" }; 
     string[] resolvedToClosedReasons = activeToResolvedReasons; 
     string[] closedToActiveReasons = { "Reactivated", "Regression" }; 
     string[] activeToClosedReasons = activeToResolvedReasons; 

     cmbBugReason.Items.AddRange(activeToResolvedReasons); 
     // Set the default reason according to change of state of the work item. 
     if (cmbBugState.SelectedItem.ToString() == "Resolved") 
     { 
      cmbBugReason.Enabled = true; 
      cmbBugReason.SelectedItem = activeToResolvedReasons[0]; 
     } 
     if (cmbBugState.SelectedItem.ToString() == "Closed") 
     { 
      cmbBugReason.Enabled = true; 
      cmbBugReason.SelectedItem = activeToResolvedReasons[1]; 
     } 
    } 

誰能告訴如何處理的形式對這些事件?

謝謝, 塔拉。

回答

1

我試過GetNextState。對於我所需要的,它永遠不夠可靠。

所以我「滾我自己」已經工作對我非常好,當我從國家「A」移動到狀態「B」狀態轉換的代碼。這有點長,但它應該有你在尋找的東西。

作爲一個方面說明:由於此不使用GetNextState方法它必須以某種方式獲取下一個狀態。它這樣做的方式是下載有問題的工作項類型的XML。它解析出來並用它來創建一個Transition列表(_ allTransistions)。

在2010 TFS的權限級別需要做到這一點是:團隊基礎管理員項目管理員。 (請注意,在TFS 2008和2005中,所有有效的用戶都可以這樣做。)

使用此代碼的完整代碼可以在codeplex上的TFS Aggregator項目的WorkItemHelpers.cs文件中找到。

public static void TransitionToState(this WorkItem workItem, string state, string commentPrefix) 
{ 
    // Set the sourceWorkItem's state so that it is clear that it has been moved. 
    string originalState = (string)workItem.Fields["State"].Value; 

    // Try to set the state of the source work item to the "Deleted/Moved" state (whatever is defined in the file). 

    // We need an open work item to set the state 
    workItem.TryOpen(); 

    // See if we can go directly to the planned state. 
    workItem.Fields["State"].Value = state; 


    if (workItem.Fields["State"].Status != FieldStatus.Valid) 
    { 
     // Revert back to the orginal value and start searching for a way to our "MovedState" 
     workItem.Fields["State"].Value = workItem.Fields["State"].OriginalValue; 

     // If we can't then try to go from the current state to another state. Saving each time till we get to where we are going. 
     foreach (string curState in workItem.Type.FindNextState((string)workItem.Fields["State"].Value, state)) 
     { 
      string comment; 
      if (curState == state) 
       comment = commentPrefix + Environment.NewLine + " State changed to " + state; 
      else 
       comment = commentPrefix + Environment.NewLine + " State changed to " + curState + " as part of move toward a state of " + state; 

      bool success = ChangeWorkItemState(workItem, originalState, curState, comment); 
      // If we could not do the incremental state change then we are done. We will have to go back to the orginal... 
      if (!success) 
       break; 
     } 
    } 
    else 
    { 
     // Just save it off if we can. 
     string comment = commentPrefix + "\n State changed to " + state; 
     ChangeWorkItemState(workItem, originalState, state, comment); 

    } 
} 
private static bool ChangeWorkItemState(this WorkItem workItem, string orginalSourceState, string destState, String comment) 
{ 
    // Try to save the new state. If that fails then we also go back to the orginal state. 
    try 
    { 
     workItem.TryOpen(); 
     workItem.Fields["State"].Value = destState; 
     workItem.History = comment; 
     workItem.Save(); 
     return true; 
    } 
    catch (Exception) 
    { 
     // Revert back to the original value. 
     workItem.Fields["State"].Value = orginalSourceState; 
     return false; 
    } 
} 

/// <summary> 
/// Used to find the next state on our way to a destination state. 
/// (Meaning if we are going from a "Not-Started" to a "Done" state, 
/// we usually have to hit a "in progress" state first. 
/// </summary> 
/// <param name="wiType"></param> 
/// <param name="fromState"></param> 
/// <param name="toState"></param> 
/// <returns></returns> 
public static IEnumerable<string> FindNextState(this WorkItemType wiType, string fromState, string toState) 
{ 
    var map = new Dictionary<string, string>(); 
    var edges = wiType.GetTransitions().ToDictionary(i => i.From, i => i.To); 
    var q = new Queue<string>(); 
    map.Add(fromState, null); 
    q.Enqueue(fromState); 
    while (q.Count > 0) 
    { 
     var current = q.Dequeue(); 
     foreach (var s in edges[current]) 
     { 
      if (!map.ContainsKey(s)) 
      { 
       map.Add(s, current); 
       if (s == toState) 
       { 
        var result = new Stack<string>(); 
        var thisNode = s; 
        do 
        { 
         result.Push(thisNode); 
         thisNode = map[thisNode]; 
        } while (thisNode != fromState); 
        while (result.Count > 0) 
         yield return result.Pop(); 
        yield break; 
       } 
       q.Enqueue(s); 
      } 
     } 
    } 
    // no path exists 
} 

private static readonly Dictionary<WorkItemType, List<Transition>> _allTransistions = new Dictionary<WorkItemType, List<Transition>>(); 

/// <summary> 
/// Deprecated 
/// Get the transitions for this <see cref="WorkItemType"/> 
/// </summary> 
/// <param name="workItemType"></param> 
/// <returns></returns> 
public static List<Transition> GetTransitions(this WorkItemType workItemType) 
{ 
    List<Transition> currentTransistions; 

    // See if this WorkItemType has already had it's transistions figured out. 
    _allTransistions.TryGetValue(workItemType, out currentTransistions); 
    if (currentTransistions != null) 
     return currentTransistions; 

    // Get this worktype type as xml 
    XmlDocument workItemTypeXml = workItemType.Export(false); 

    // Create a dictionary to allow us to look up the "to" state using a "from" state. 
    var newTransistions = new List<Transition>(); 

    // get the transistions node. 
    XmlNodeList transitionsList = workItemTypeXml.GetElementsByTagName("TRANSITIONS"); 

    // As there is only one transistions item we can just get the first 
    XmlNode transitions = transitionsList[0]; 

    // Iterate all the transitions 
    foreach (XmlNode transitionXML in transitions) 
    { 
     // See if we have this from state already. 
     string fromState = transitionXML.Attributes["from"].Value; 
     Transition transition = newTransistions.Find(trans => trans.From == fromState); 
     if (transition != null) 
     { 
      transition.To.Add(transitionXML.Attributes["to"].Value); 
     } 
     // If we could not find this state already then add it. 
     else 
     { 
      // save off the transistion (from first so we can look up state progression. 
      newTransistions.Add(new Transition 
      { 
       From = transitionXML.Attributes["from"].Value, 
       To = new List<string> { transitionXML.Attributes["to"].Value } 
      }); 
     } 
    } 

    // Save off this transition so we don't do it again if it is needed. 
    _allTransistions.Add(workItemType, newTransistions); 

    return newTransistions; 
}