VSTF的工作项WorkItem本身也包含WorkFlow的信息,也就是说在一个WorkItem状态、指派人员等信息改变过程中要遵循一定的规则,这个规则可以由TFS(Team Foundation Server)的管理员作修改和扩充。那么,如果要限制程序员根据工作项迁入代码时也要满足一定条件,比如工作项状态必须是“已完成”,否则不允许迁入,该如何做到呢?幸运的是,Microsoft已经在TFS 对象模型中为我们提供了这样的控制接口。
实际上,我们要做的仅是编写一个简单的自定义迁入策略类,然后将生成的dll注册到VSTF客户机的注册表中即可。该类必须继承自PolicyBase,它存在于Microsoft.TeamFoundation.VersionControl.Client命名空间中。如下图所示。
1. PolicyBase公共属性
- CanEdit :获取该策略是否包含配置界面的标识。True,表明该策略包含配置界面。
- Description: 获取该策略的描述。
- InstallationInstructions: 获取或设置策略安装到目标机说明的文本描述。当策略的dll没有被注册时会在
策略失败窗口中显示该信息。
- Type: 获取描述该策略的字符串。
- TypeDescription: 获取该策略类型的描述。
2. PolicyBase公共方法
- Activate: 如果用户双击策略失败信息时该方法被调用。
- DisplayHelp: 当用户操作窗口中策略失败提示被激活时用户按F1该方法被调用,即显示帮助信息。
- Edit :用以向用户显示有关迁入策略的对话框或其他界面窗口。
- Evaluate :执行通常的策略评估。该方法其实是自定义迁入策略的核心,迁入控制规则必须在该方法的重写中实现。
3. PolicyBase公共事件
- PolicyStateChanged:策略状态改变事件,一般不使用,除非你想动态使用策略。
4. 检查工作项状态迁入策略实现
前面已经提到,自定义迁入策略实现的关键在于重写Evaluate 方法,当然你也可以根据需要重写其他属性和方法,这样在策略失败时可以在迁入对话框上显示一些定制的信息。迁入前判断工作项状态的代码实现如下所示的,当使用该策略时仅当关联工作项状态是“已完成”时才能够迁入代码。
/// <summary>
/// This method performs the actual evaluation. It is called by the policy framework at various points in time
/// when policy should be evaluated. In this example, we invoke this method ourselves when various asyc
/// events occur that may have invalidated the current list of failures.
/// </summary>
/// <returns></returns>
public override PolicyFailure[] Evaluate()
{
List<PolicyFailure> list = new List<PolicyFailure>();
bool flag = false;
foreach (WorkItemCheckinInfo info in base.PendingCheckin.WorkItems.CheckedWorkItems)
{
if (info.WorkItem.Type.Name == "Founction Exception")
{
if (info.WorkItem.State == "Finished")
{
flag = true;
}
}
}
if (!flag)
{
list.Add(new PolicyFailure("The state of this type workitem unmatch this policy!", this));
}
return list.ToArray();
}
上面代码中 List<PolicyFailure>实际上是一个迁入策略失败信息的列表,最终会显示在用户代码迁入对话框的提示信息中,如下图所示。
5. 更近一步
如果说上面的自定义迁入策略代码还是在使用工作项对象的属性,不够灵活,那么我们还可以将工作项查询语句引入进来,对查询到的工作项集合进行操作,从而保证迁入策略得到最大限度的扩充,定制各种符合用户不同需要的迁入规则。下例就演示了一种迁入时必须关联代码Review工作项的自定义迁入策略。
public override PolicyFailure[] Evaluate()
{
List<PolicyFailure> list1 = new List<PolicyFailure>();
bool flag1 = false;
bool flag2 = true;
bool flag3 = true;
foreach (WorkItemCheckinInfo info1 in base.PendingCheckin.WorkItems.CheckedWorkItems)
{
if (info1.WorkItem.get_Type().get_Name() == "Code Review")
{
flag1 = true;
if ((info1.WorkItem.get_State() == "Closed") || (info1.WorkItem.get_State() == "Resolved"))
{
if (((string) info1.WorkItem.get_Fields().get_Item("Resolution").get_Value()) == "Approved")
{
foreach (Link link1 in info1.WorkItem.get_Links())
{
RelatedLink link2 = link1 as RelatedLink;
if (link2 != null)
{
object[] objArray1 = new object[] { link2.get_RelatedWorkItemId().ToString(CultureInfo.InvariantCulture) };
WorkItemCollection collection1 = info1.WorkItem.get_Store().Query(string.Format(CultureInfo.InvariantCulture, "SELECT [System.Id] FROM WorkItems WHERE [System.Id] = '{0}' AND [System.WorkItemType] = 'Bug'", objArray1));
if ((collection1.get_Count() == 1) && (collection1.get_Item(0).get_State() != "Closed"))
{
flag2 = false;
}
}
}
if (info1.WorkItem.get_State() == "Resolved")
{
try
{
info1.WorkItem.set_State("Closed");
info1.WorkItem.Save();
}
catch
{
}
}
}
else
{
flag3 = false;
}
}
}
}
if (!flag1)
{
list1.Add(new PolicyFailure("There were no associated Code Review Work Items for this check in.", this));
}
if (!flag2)
{
list1.Add(new PolicyFailure("Associated bugs in Code Review work items were not closed.", this));
}
if (!flag3)
{
list1.Add(new PolicyFailure("Not all associated Code Review work items were marked as Approved.", this));
}
return list1.ToArray();
}
注:以上代码摘自http://www.gotdotnet.com/codegallery提供的范例。
6. 自定义迁入策略的部署
- 将检查工作项状态的迁入策略代码编译生成dll,copy到VSTF客户机上的某个目录中。
- 在注册表[HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/VisualStudio/8.0/TeamFoundation/SourceControl/Checkin Policies]下建字符串项,名称为CheckForStatePolicy(dll名称),值为该dll的物理路径如“c://CheckForStatePolicy.dll”。
- 在VSTF中右键点击“项目”,在“团队项目设置”中选择“源代码管理设置”,点击“迁入策略”标签页并在“添加迁入策略”窗口中选择“Check For State Policy”项。