As a matter of self-inflicted education I’ve decided to create a simple Visual Studio Add-In for exporting and importing Areas and Iterations. I’m not sure how much practical use such a utility will receive but it offers several areas for advancing my knowledge and, hopefully, helping a few other people out along the way.
Although I haven’t completed the utility yet, I have completed some of the initial utility code. I thought I would create a few posts as I progress in case anyone can make use of the examples. For this post, I am posting the code used to retrieve the list of “areas” from a Team Foundation Server.
With that said, here is some example code for saving the list of Areas defined for a given Team Foundation Server and Team Project. The code is a little verbose but should be relatively simple to follow.
using System;using System.Collections.Generic;
using System.IO;
using System.Xml;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Server;
namespace TestConsole
{
public class ExportAreas
{
private TeamFoundationServer _teamFoundationServer;
private ICommonStructureService _commonStructureService;
/// <summary>
/// Exports the "areas" defined within the specified team foundation server.
/// </summary>
/// <param name="teamFoundationServer">The team foundation server.</param>
/// <param name="projectName">The name of the team project to export areas from.</param>
/// <param name="filename">The filename to save the exported areas to.</param>
public void Export(string teamFoundationServer, string projectName, string filename)
{
_teamFoundationServer = TeamFoundationServerFactory.GetServer(teamFoundationServer);
_commonStructureService = (ICommonStructureService)_teamFoundationServer.GetService(typeof(ICommonStructureService));
List<HierarchyNode> nodeList = new List<HierarchyNode>();
// Locate root structure node for "Area"
NodeInfo areaNode = GetAreaRootNode(projectName);
//String node = _commonStructureService.CreateNode("TEST Node...", areaUri);
// Retrieve the Area nodes XML
XmlElement nodes = _commonStructureService.GetNodesXml(new string[] { areaNode.Uri }, true);
// Recursively build out the list of Area nodes
BuildHierarchicalList(nodes.ChildNodes[0], nodeList, 0);
// Save the areas to the designated text file
StreamWriter writer = new StreamWriter(filename, false);
foreach (HierarchyNode node in nodeList)
{
writer.WriteLine(new string('\t', node.IndentLevel) + node.Name);
}
writer.Close();
}
/// <summary>
/// Recursively builds a hierarchical list based on the root node passed in.
/// </summary>
/// <param name="rootNode">The root node to start with.</param>
/// <param name="targetList">The target list to add each <see cref="HierarchyNode"/> instance to. This
/// list must be instantaited on the first call.</param>
/// <param name="indentLevel">The indent level - pass in 0 for the initial call.</param>
private void BuildHierarchicalList(XmlNode rootNode, List<HierarchyNode> targetList, int indentLevel)
{
HierarchyNode listNode;
if (rootNode.Name == "Children")
{
for (int index = 0; index < rootNode.ChildNodes.Count; index++)
{
BuildHierarchicalList(rootNode.ChildNodes[index], targetList, indentLevel);
}
}
else
{
listNode = new HierarchyNode()
{
Uri = rootNode.Attributes["NodeID"].Value,
Name = rootNode.Attributes["Name"].Value,
ParentUri = GetAttribute(rootNode, "ParentID"),
Path = rootNode.Attributes["Path"].Value,
ProjectUri = rootNode.Attributes["ProjectID"].Value,
StructureType = rootNode.Attributes["StructureType"].Value,
IndentLevel = indentLevel
};
targetList.Add(listNode);
if (rootNode.HasChildNodes)
{
for (int index = 0; index < rootNode.ChildNodes.Count; index++)
{
BuildHierarchicalList(rootNode.ChildNodes[index], targetList, indentLevel + 1);
}
}
}
}
/// <summary>
/// Gets the specified attribute from <paramref name="node"/>..
/// </summary>
/// <param name="node">The node containing the attribute collection.</param>
/// <param name="attributeName">The name of the attribute to retrieve.</param>
/// <returns>The value of the specified attribute if it exists; otherwise, an empty string is returned.</returns>
private string GetAttribute(XmlNode node, string attributeName)
{
for (int index = 0; index < node.Attributes.Count; index++)
{
if (string.Equals(node.Attributes[index].Name, attributeName, StringComparison.InvariantCultureIgnoreCase))
{
return node.Attributes[index].Value;
}
}
return string.Empty;
}
/// <summary>
/// Gets the area root node.
/// </summary>
/// <param name="projectName">The name of the team project to retrieve the root node from.</param>
/// <returns>A <see cref="NodeInfo"/> object referencing the root area node.</returns>
private NodeInfo GetAreaRootNode(string projectName)
{
ProjectInfo projectInfo = _commonStructureService.GetProjectFromName(projectName);
return LocateStructureByType(projectInfo.Uri, "ProjectModelHierarchy");
}
/// <summary>
/// Locates the desired structure based on structure type.
/// </summary>
/// <param name="projectUri">The project URI to retrieve the structure from.</param>
/// <param name="structureType">Type of the structure to retrieve.</param>
/// <returns>A <see cref="NodeInfo"/> object referencing the root of the desired structure.</returns>
private NodeInfo LocateStructureByType(String projectUri, String structureType)
{
NodeInfo[] nodes = _commonStructureService.ListStructures(projectUri);
foreach (NodeInfo node in nodes)
{
if (node.StructureType == structureType)
{
return node;
}
}
return null;
}
}
/// <summary>
/// This class is used to hold information pertaining to exported areas or iterations.
/// </summary>
class HierarchyNode
{
public string Uri { get; set; }
public string Name { get; set; }
public string ParentUri { get; set; }
public string Path { get; set; }
public string ProjectUri { get; set; }
public string StructureType { get; set; }
public int IndentLevel { get; set; }
}
}
To use the above sample code, just instantiate the ExportAreas class and call the Export method passing in the desired argument values.
2 comments:
Jeff,
Have a look at this tool
http://msmvps.com/blogs/vstsblog/archive/2007/07/07/copy-area-and-interation-structure-using-the-area-import-export-tool.aspx
Eugene, thanks for the link. I've seen other examples regarding Areas and Iterations as well. This exercise is basically an excuse to create a simple TFS utility and provide integration with Visual Studio as an add-in. I realize I'm re-inventing the wheel (to a certain extent) but it seems like a great way to sharpen some skills :-)
Post a Comment