Populating Lists on Work Items from an External Source
A frequently asked question about work item type definitions is: “How do I populate the list of allowed/suggested/prohibited values from an external source?” This can be achieved by using global lists – named lists of values that can be shared by any work item type on the Team Foundation Server.
This post shows an example of how to use global lists. In this example, you run an online retail shop with different pages dedicated to different product categories. You use a work item type called “MyType” to track customer requests for changes to the site, additions to the selection, or other issues. You want to classify your customer request based on the category of products that they’re associated with so that you can report on how many requests you’re getting per category. To do this, your work items have a product category field with a list of allowed values. The XML for your work item type looks like this.
<FIELD name="Product Categories" refname="MyCompany.ProductCategeroies" type="String" reportable="dimension">
<HELPTEXT>Product Category</HELPTEXT>
<ALLOWEDVALUES>
<LISTITEM value="Books" />
<LISTITEM value="Electronics" />
<LISTITEM value="Kitchenware" />
<LISTITEM value="Toys" />
</ALLOWEDVALUES>
</FIELD>
However, the product categories on your web site constantly change forcing you to manually update your work item type all the time. Instead, you decide to harness the power of global lists to maintain the product categories list. You author a tool that checks your product inventory database for updates and mirrors those updates in a global list on the Team Foundation Server. Then, you change your work item type to use the values of this global list to dynamically populate the entries in the Product Categories field. Now, when a new DVD category is added to your product database, your tool automatically updates the categories on the Team Foundation Server and your work items pick up these changes from the global list. In fact, you can have as many work item types as you want leverage this global list.
Now, before you get started, make sure to download the Beta 2 Extensibility Kit and unzip “Authoring Work Item Types.zip” into the ~\Program Files\Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies folder on your client machine to get the tools you need. Also, check out the documentation in the “Work Item Tracking” and “Work Item Types” folders for detailed info about the work item tracking object model, the type definitional language, and global lists.
Step 1: Create a global list
The first step is to create a global list that will hold all the product categories. To do this, create an XML file like below and save it as “gl.xml”.
<?xml version="1.0" encoding="utf-8" ?>
<GLOBALLISTS>
<GLOBALLIST name="Products">
<LISTITEM value="Books" />
<LISTITEM value="Electronics" />
<LISTITEM value="Kitchenware" />
<LISTITEM value="Toys" />
</GLOBALLIST>
</GLOBALLISTS>
The GLOBALLIST element declares a new global list called “Products.” The LISTITEM elements add string values to this list. Note that you can add as many global lists as you want and that each list is a flat structure (no hierarchies).
Next, import the global list onto the Team Foundation Server. You must be run this tool as a Namespace Administrator.
- Open a command prompt and CD into your ~\Program Files\Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies folder
- Run glimport to import the list
glimport /f gl.xml /t MyTFSName
- Run glexport to make sure the list got uploaded correctly. You should see the XML you uploaded on screen.
witexport /f MyType.xml /t MyTFSName /p MyTeamProject /n MyType
Now you have a global list on the server ready to be referenced by work item types and updated by your tool.
Step 2: Customize your work item type to reference the list
The next step is to customize your work item type to use this global list instead of hard coded list values. Export your existing work item type and edit the XML to refer to the list for field. The steps like this.
- Open a command prompt and CD into your ~\Program Files\Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies folder
- Run witexport to export your work item type
witexport /f MyTypeBeforeChanges.xml /t MyTFSName /p MyTeamProject /n MyType
- Edit the XML for your “Product Categories” field to populate it with items from the global list “Products.”
<FIELD name="Product Categories" refname="MyCompany.ProductCategeroies" type="String" reportable="dimension">
<HELPTEXT>Product Category</HELPTEXT>
<ALLOWEDVALUES>
<GLOBALLIST name="Products" />
</ALLOWEDVALUES>
</FIELD>
- Save the type xml as “MyTypeAfterChanges.xml” and reimport it.
witimport /f MyTypeAfterChanges.xml /t MyTFSName /p MyTeamProject
When you create new work items of this type, the Product Categories field will show the same list values. However, these will be pulled from the global list.
Note: In the Beta 2 build, there is a bug where the global list name will show up in the drop down list – i.e. “*Products” will appear in the Product Categories field in the example. This will be fixed in our next community drop.
Step 3: Update the global list from an external source
Now that everything is setup up, you can write a tool that syncs changes from your inventory database to the “Products” global list. You write this tool using the Work Item Tracking Object Model (see “Work Item Object Model Sample.zip” in the Beta 2 Extensibility Kit) and run it under a TFS Service Account so that it can make changes to the server.
You set the tool to run regularly as an NT Service and include the following code snippet to update the global list.
//read your inventory DB for all product categories
//create a string “categories” that contains XML in the following format
//with a LISTITEM element for each category read from the DB
//
//<?xml version="1.0" encoding="utf-8" ?>
//<GLOBALLISTS>
//<GLOBALLIST name="Products">
//<LISTITEM value="Books" />
//<LISTITEM value="Electronics" />
//<LISTITEM value="Kitchenware" />
//<LISTITEM value="Toys" />
//</GLOBALLIST>
//</GLOBALLISTS>
//get the work item store from the TeamFoundationServer
TeamFoundationServer tfs = new TeamFoundationServer(server); //server – string for server name
WorkItemStore store = (WorkItemStore)tfs.GetService(typeof(WorkItemStore));
//import the global list defined in string “categories” to the work item store
store.ImportGlobalLists(lists);
Once this tool is running, changes in the inventory database are mirrored in the Product Category field on your work item.
Sample XML
Here are sample XMLs you can use for this exercise.
Keep in Mind
Here are some things to keep in mind while working with global lists.
- Global lists can only be modified by members of Namespace Administrators and Service Accounts groups.
- If a work item refers to a list item that is later removed in a global list update, the work item will still show the removed value next time it’s opened. However, users will have to select a new value before saving updates to this item. Note this is the same behavior with any type of list value or field constraint where previously legal values become invalid due to a work item type change.
- Global list names are unique to a Team Foundation server and can be up to 254 Unicode characters long.
- When a global list is updated, clients will have to refresh their metadata to get the latest values. This is done by clicking the “Refresh” button on the Team Explorer in the IDE.
Until Next Time,
Ling Bao and Aliaksei Baturytski
Visual Studio Team Foundation Server