工作项目状态的转换工作流程

问题描述:

我正在开发一个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]; 
     } 
    } 

谁能告诉如何处理的形式对这些事件?

谢谢, 塔拉。

我试过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; 
}