16
votes

I'm trying to write a project reporting tool in WPF / C#. I want to access all the project names on our TFS (Team Foundation Server), and then display statistics for each work item in a given project.

I've got the project names, but getting the actual work items is what's giving me a hard time. Here's what I've got so far:

public const string tfsLocation = "http://whatever";

// get the top list of project names from the team foundation server
public List<string> LoadProjectList()
{
    var tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(tfsLocation));
    var workItemStore = new WorkItemStore(tpc);
    var projects = (from Project project in workItemStore.Projects select project.Name).ToList();

    return projects;
}

public string GetProjectInfo(string targetProject)
{
    string info = String.Empty;

    var tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(tfsLocation));
    var workItemStore = new WorkItemStore(tpc);

    foreach (Project project in workItemStore.Projects)
    {
        if (project.Name == targetProject)
        {
            info += String.Format("Project: {0}\n\n", project.Name);

            info += "Work Item Types:\n";
            foreach (WorkItemType item in project.WorkItemTypes)
            {
                info += String.Format("-   {0}\n", item.Name);
                info += String.Format("    -   Description: {0}\n", item.Description);
                info +=               "    -   Field Definitions:\n";

                foreach (FieldDefinition field in item.FieldDefinitions)
                {
                    info += String.Format("        -   {0}\n", field.Name);
                }
                info += "\n";
            }
        }
    }

    return info;
}

GetProjectInfo sends back some helpful info about what's in each project, but so far it looks like I'm only seeing the definitions of what the WorkItems consist of, and not the actual WorkItems themselves. I think the programming I've written is looking in the wrong place.

From Microsoft's definition of WorkItem, (http://msdn.microsoft.com/en-us/library/microsoft.teamfoundation.workitemtracking.client.workitem.aspx) it looks like it's inside WorkItemTracking.Client, but not inside the WorkItemStore, and I'm not sure where to go to access it.

FINAL VERSION:

Here's the updated version of my function, after referencing the below answer. This just returns a long string of the work item names with new lines between, for printing out, which is all I'm trying to get working (for now).

public string GetProjectInfo(string targetProject)
{
    string info = String.Empty;

    var tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(tfsLocation));
    WorkItemStore workItemStore = new WorkItemStore(tpc);

    Query query = new Query(workItemStore, "SELECT * FROM WorkItems WHERE [System.TeamProject] = @project", new Dictionary<string, string>() { { "project", targetProject } });

    WorkItemCollection wic = query.RunQuery();

    foreach (WorkItem item in wic)
    {
        info += String.Format("{0}\n", item.Title);
    }

    return info;
}
2
I up vote it, sounds interesting. Help someone work on this.mybirthname

2 Answers

13
votes

You need to use WIQL queries to get actual work items you are interested in, e.g. to get all work items for a particular project:

using Microsoft.TeamFoundation.WorkItemTracking.Client;

Query query = new Query(
     workItemStore, 
     "select * from issue where System.TeamProject = @project",
     new Dictionary<string, string>() { { "project", project.Name } }
);

var workItemCollection = query.RunQuery();
foreach(Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem workItem in workItemCollection) 
{
   /*Get work item properties you are interested in*/
   foreach(Microsoft.TeamFoundation.WorkItemTracking.Client.Field field in workItem.Fields)
   {
      /*Get field value*/
      info += String.Format("Field name: {0} Value: {1}\n", field.Name, field.Value);
   }
}
0
votes

I do need to extract the linked Work Item (Testcases) as well with the Bug. I have created a query and it extracts both. But my issue is while i print the Work Items Fields, All of the prints separately, with no trace of which Bug is linked to which Testcase. How can I achieve that.

public async Task<IList<WorkItem>> QueryOpenBugs(string project)
    {
        var credentials = new VssBasicCredential(string.Empty, this.personalAccessToken);

        // create a wiql object and build our query
        var wiql = new Wiql()
        {
            // NOTE: Even if other columns are specified, only the ID & URL are available in the WorkItemReference
            //Query = "Select [Id] " +
            //        "From WorkItems " +
            //        "Where [Work Item Type] = 'Bug' " +
            //        "And [System.TeamProject] = '" + project + "' " +
            //        "And [System.State] = 'Resolved' " +
            //        "Order By [State] Asc, [Changed Date] Desc",

            Query = "Select [System.Id],[System.WorkItemType],[System.Title]" +
                    "From workitemLinks " +
                    "Where ([Source].[System.WorkItemType] = 'Bug' " +
                    "And [Source].[System.TeamProject] = '" + project + "' " +
                    "And [Source].[System.State] = 'Resolved' )" +
                    "And ([Target].[System.TeamProject] = '" + project + "' " +
                    "And [Target].[System.WorkItemType] = 'Test Case' )",



        };

        using (var httpClient = new WorkItemTrackingHttpClient(this.uri, credentials))
        {
            // execute the query to get the list of work items in the results
            var result = await httpClient.QueryByWiqlAsync(wiql).ConfigureAwait(false);
            var ids = result.WorkItemRelations.Select(item => item.Target.Id).ToArray();
     
            // some error handling
            if (ids.Length == 0)
            {
                return Array.Empty<WorkItem>();
            }

            // build a list of the fields we want to see
            var fields = new[] { "System.Id", "System.Title", "System.State" , "System.IterationPath", "System.Tags", "Microsoft.VSTS.Common.StateChangeDate", "System.WorkItemType", "Microsoft.VSTS.TCM.AutomationStatus"};

            // get work items for the ids found in query
            return await httpClient.GetWorkItemsAsync(ids, fields, result.AsOf).ConfigureAwait(false);
        }
    }

    /// <summary>
    ///     Execute a WIQL (Work Item Query Language) query to print a list of open bugs.
    /// </summary>
    /// <param name="project">The name of your project within your organization.</param>
    /// <returns>An async task.</returns>
    public async Task PrintOpenBugsAsync(string project)
    {
        var workItems = await this.QueryOpenBugs(project).ConfigureAwait(false);

        Console.WriteLine("Query Results: {0} items found", workItems.Count);
        
        // loop though work items and write to console
        
        //Select - BugID , TestCaseID , TestSuiteID{} , ResolvedDate , AutomationStatus{}
        foreach (var workItem in workItems)
        {

            string WorkItemType = (string)workItem.Fields["System.WorkItemType"];
            if (WorkItemType == "Bug")
            {
                Console.WriteLine("The Bugs are:\n\n");
                Console.WriteLine(
                "{0}\t{1}\t{2}\t{3}\t{4}",
                workItem.Id,
                workItem.Fields["System.Title"],
                workItem.Fields["System.State"],
             //   workItem.Fields["System.RelatedLinks"],
                workItem.Fields["Microsoft.VSTS.Common.StateChangeDate"],
                workItem.Fields["System.WorkItemType"]);

                Console.WriteLine("\n");
            }
            else
            {
                Console.WriteLine("The TestCases are:\n\n");
                Console.WriteLine(
                "{0}\t{1}\t{2}\t{3}\t{4}",
                workItem.Id,
                workItem.Fields["System.Title"],
                workItem.Fields["System.State"],
                workItem.Fields["Microsoft.VSTS.TCM.AutomationStatus"],
                workItem.Fields["System.WorkItemType"]);
       
                Console.WriteLine("\n");
            }