Рабочие процессы перехода состояний рабочего элемента
Я занимаюсь разработкой приложения для Windows, которое выполняет некоторые распространенные задачи TFS с использованием API бета-версии 2010 (например, создание новых командных проектов, новых рабочих элементов, выборочная сборка и т. Д.).
В процессе редактирования существующих рабочих элементов я должен иметь возможность автоматически устанавливать значения поля "Причина" в соответствии с изменением состояния WI (имитируя Visual Studio). (например)- Когда я редактирую ошибку, когда состояние изменяется с Активного на Решенный, Причиной по умолчанию является "Фиксированный" и аналогичным образом Причиной по умолчанию = "Отложенный", когда состояние переходит от Активного к Закрытому. (Как определено в XML-файле определения типа рабочего элемента.) Этот переход легко захватить и реализовать в простом обработчике событий в форме, поскольку начальное состояние будет активным при первом редактировании ошибки.
Я хочу знать, как реализовать оставшиеся переходы, такие как Resolved to Closed (Reason=Fixed), Resolved to Active (Reason= Тест не пройден /Not fixed) или Closed to Active (Reason=Reacactive /Regression).
Я знаю, что есть метод с именем WorkItem.GetNextState(current_state,action), но это не помогает, так как требует определенного действия.
То, что я сделал до сих пор, показано ниже:
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. Это никогда не было достаточно надежным для того, что мне было нужно.
Поэтому я "свернул свой" код перехода состояний, который мне очень помог, когда я перехожу из состояния "А" в состояние "В". Это немного долго, но в нем должно быть то, что вы ищете.
В качестве примечания: поскольку здесь не используется метод GetNextState, он должен каким-то образом получить следующее состояние. Это происходит путем загрузки XML соответствующего типа рабочего элемента. Он анализирует это и использует это для создания списка переходов (_allTransistions
).
Для этого необходимы уровни разрешений в TFS 2010: администраторы Team Foundation или администраторы проекта. (Как примечание, в TFS 2008 и 2005 все действительные пользователи могли сделать это.)
Полный код, который использует это, можно найти в файле WorkItemHelpers.cs в проекте TFS Aggregator на codeplex.
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;
}