Querying on WorkItem Links through the API

In Rosario Team Foundation Server, significant new functionality is added for liking work item types
https://blogs.msdn.com/bharry/archive/2007/08/06/work-item-tracking-enhancements-in-the-aug-rosario-ctp.aspx

https://blogs.msdn.com/teams_wit_tools/archive/tags/Rosario/default.aspx

Here, I will try to describe how to query on link through the work item API.

First let’s get a list of work item types in the server.

After the obligatory logging in into the TFS and getting the Work Item Store, you need to go to a
specific project and then get the work item types defined for that project. Here is a code snippet
that prints all the work item types in a project

TeamFoundationServer TFS;

WorkItemStore WIStore;

TFS = TeamFoundationServerFactory.GetServer(https://"<your tfs server>:<port typically = 8080>");

WIStore = TFS.GetService(typeof(WorkItemStore)) as WorkItemStore;

Project TeamProject = WIStore.Projects["MyProject"];

WorkItemTypeCollection workItemTypes = TeamProject.WorkItemTypes;

foreach (WorkItemType workItemType in workItemTypes)

{

    Console.WriteLine(workItemType.Name);

}

 

Now that we know how to get the work item types, we can look at the work item link types

While the work item types are project specific, work item link types are global to all projects.
The code snippet below shows how to get the work item link types.

WorkItemLinkTypeCollection worItemLinkTypes = WIStore.WorkItemLinkTypes;

foreach (WorkItemLinkType workItemLinkType in worItemLinkTypes)

{

    Console.WriteLine("-----------------------------------------");

    Console.WriteLine("Base ReferenceName: {0}", workItemLinkType.BaseReferenceName);

    Console.WriteLine("Id: {0}", workItemLinkType.Id);

    Console.WriteLine("Name: {0}", workItemLinkType.Name);

    Console.WriteLine("IsDirectional: {0}", workItemLinkType.IsDirectional);

    Console.WriteLine("IsForwardLink: {0}", workItemLinkType.IsForwardLink);

    Console.WriteLine("IsNonCircular: {0}", workItemLinkType.IsNonCircular);

    Console.WriteLine("IsOneToMany: {0}", workItemLinkType.IsOneToMany);

    Console.WriteLine("LinkTopology: {0}", workItemLinkType.LinkTopology.ToString());

    WorkItemLinkType workItemLinkTypeReverse = workItemLinkType.Reverse;

    Console.WriteLine("\t....");

    Console.WriteLine("\tId: {0}", workItemLinkTypeReverse.Id);

    Console.WriteLine("\tName: {0}", workItemLinkTypeReverse.Name);

    Console.WriteLine("\tIsDirectional: {0}", workItemLinkTypeReverse.IsDirectional);

    Console.WriteLine("\tIsForwardLink: {0}", workItemLinkTypeReverse.IsForwardLink);

    Console.WriteLine("\tIsNonCircular: {0}", workItemLinkTypeReverse.IsNonCircular);

    Console.WriteLine("\tIsOneToMany: {0}", workItemLinkTypeReverse.IsOneToMany);

    Console.WriteLine("\tLinkTopology: {0}", workItemLinkTypeReverse.LinkTopology.ToString());

}

 

When you run the code it prints something like
Base ReferenceName: Microsoft.VSTS.Common.Produces

Id: 4

Name: Produces

IsDirectional: True

IsForwardLink: True

IsNonCircular: True

IsOneToMany: False

        ....

        Id: -4

        Name: Is Produced By

        IsDirectional: True

        IsForwardLink: False

        IsNonCircular: True

        IsOneToMany: False

 

So it seems that a “Relation” is represented by two “Work Item Link Type” instances. Each direction of the “relation” is a work item link type. The other direction is the “reverse” work item link type.

Let’s take a concrete example:
A feature is produced by one or more deliverables. A deliverable produces one or more features.
This relation is a many to many relation. This relation has a BaseReferenceName – “Produces” and is represented by two work item link type instances.

 

Features Is Produced by Deliverable

Deliverable Produces Feature.

Each work item link type has a “Reverse” property that points to its counterpart.

 

Once I got the work item types and work item link types, I tried to see if there are constraints on what link types a work item type can have. For example, can a feature have a parent as a deliverable?
Can a deliverable have a parent Task? It seems that given a set of work item types, only certain relations are valid for a work item of a give type. Unfortunately, as of now, this feature is not implemented.
So any work item type can have any relation or link type as you desire. The team foundation server team is apparently thinking about adding this in the subsequent CTP releases, but nothing is guaranteed.

 

Querying on Links

Let’s take a look at how to query the work item links.
The WorkItemLinks property will give you all the work item links. Since there could be other types of links, the BaseLinkType property is looked at to make sure that this is the work item link. By querying the WorkItemLink.SourceId and TargetId properties, you can discover what the work item is on the other side of the relation. In this case, my feature 9999, is hooked up to a deliverable via a “Produces” relation
and with a “Is Produced By “directional link.

 

WorkItem wiFeatureGroup = WIStore.GetWorkItem(9999);

foreach (WorkItemLink wiLink in wiFeatureGroup.WorkItemLinks)

{

    if (wiLink.BaseType == BaseLinkType.WorkItemLink)

    {

        if(wiLink.LinkType.BaseReferenceName == "Microsoft.VSTS.Common.Produces" && wiLink.LinkType.Name == "Is Produced By")

        {

            WorkItem wiDeliverable = WIStore.GetWorkItem(wiLink.TargetId);

            Console.WriteLine("The target WorkItemID is {0} and its type is {1}", wiDeliverable.Id, wiDeliverable.Type.Name);

        }

    }

}

Adding a link

 

Adding a link is simple. You create a link and add it to the collection, then save the work item

 

WorkItem wiFeatureGroup = WIStore.GetWorkItem(9999);

WorkItemLink wiLink = new WorkItemLink(WIStore.WorkItemLinkTypes["Is Produced By"], 9999, 8888);

wiFeatureGroup.WorkItemLinks.Add(wiLink);

wiFeatureGroup.Save();

 

 

 

Deleting a link

To delete a link, you would need to identity the link you want to delete and call the Remove() method on that. Note that you need to save the WorkItem to persist the changes.

WorkItem wiFeatureGroup = WIStore.GetWorkItem(642);

foreach (WorkItemLink wiLink in wiFeatureGroup.WorkItemLinks)

{

    if (wiLink.BaseType == BaseLinkType.WorkItemLink)

    {

        if(wiLink.LinkType.BaseReferenceName == "Microsoft.VSTS.Common.Produces" && wiLink.LinkType.Name == "Is Produced By")

        {

            WorkItem wiDeliverable = WIStore.GetWorkItem(wiLink.TargetId);

            if (wiDeliverable.Id == 887)

            {

                wiFeatureGroup.WorkItemLinks.Remove(wiLink);

                wiFeatureGroup.Save();

                break;

            }

    }

    }

}

All in all, you will generally find that querying, adding and deleting work item links are easy and intuitive.
The ability to add “relations” to work items opens tremendous flexibility and gives you the ability to write cool new applications that use the power of TFS work item store.