Problem Definition
I am about to take ownership of a TFS 2005 repository that has projects for BizTalk, SharePoint, SSIS, SSAS, different flavors of .NET, to complicate things, the customer is using different versions of visual studio to develop and maintain these solutions. To take control of the source control and successfully manage the build infrastructure it is important for me to have a view on what project types, what version of these project types, what version of visual studio, etc are being used.
Not a lot of this information is available in the TFS data warehouse, so it’s not possible to report these details from the warehouse…
Note– Download a working demo solution from here
Objective
In this blog post, I’ll walk you through using the TFS API to generate a report by querying the csproj, sln files in the source control repository. Further parsing the csproj and sln files to generate the below report…
- Name of the project/solution
- Server Path of the project/solution in the TFS repository
- Target framework version – In case of sln: VS file version, In case of csproj: the version of .NET
- Output Type – The project output type such as win exe, library, etc (Not applicable to sln)
- Project Type – Only applicable to csproj: Parse the Project Type Guid to return the project type such as WPF, MVC, Web App, C#, etc
- Reference – Only applicable to csproj: Generate a list of project references and strict version where applicable
- Last Check In Date – When was the proj or sln file last changed
- Last Changed By – Who was the proj or sln file last changed by
Walkthrough
The code solution I have implemented,
- Download and Install the TFS API, create a new WPF project and add a reference to the following dll’s. More details on how to use the TFS API here
- Use the TFS API to prompt the user to connect to TFS and select a team project (this can easily be scaled to allow multiple tpc)
privatestatic TfsTeamProjectCollection _tfs;privatestatic ProjectInfo _selectedTeamProject;privatestatic VersionControlServer _vcs;privatestatic Workspace _workspace;// Connect to TFS Using Team Project Pickerprivatestaticvoid ConnectToTfsUsingTeamProjectPicker() {// The user is allowed to select only one project var tfsPp = new TeamProjectPicker(TeamProjectPickerMode.SingleProject, false); tfsPp.ShowDialog(); // The TFS project collection _tfs = tfsPp.SelectedTeamProjectCollection;if (tfsPp.SelectedProjects.Any()) {// The selected Team Project _selectedTeamProject = tfsPp.SelectedProjects[0]; } }
- Use the TFS API to Create a temporary workspace
- Use the TFS API to perform a wildcard based get operation to ONLY download the sln and csproj files in the temp workspace
privatevoid CreateWorkspace() { _vcs = _tfs.GetService<VersionControlServer>(); var name = string.Format("{0}_{1}", "TempWs", Guid.NewGuid()); var c = new CreateWorkspaceParameters(name) { Computer = Environment.MachineName, Folders = new[] { new WorkingFolder(string.Format(@"$/{0}", _selectedTeamProject.Name), string.Format("{0}_{1}", Path.GetTempPath(), name)) }, Location = WorkspaceLocation.Local, OwnerDisplayName = Environment.UserName, WorkspaceName = name }; _workspace = _vcs.CreateWorkspace(c); tbSearchPattern.Text = string.Format("$/{0}/*.csproj | $/{0}/*.sln", _selectedTeamProject.Name); var req1 = new GetRequest(string.Format("$/{0}/*.csproj", _selectedTeamProject.Name), RecursionType.Full, VersionSpec.Latest); var req2 = new GetRequest(string.Format("$/{0}/*.sln", _selectedTeamProject.Name), RecursionType.Full, VersionSpec.Latest); _workspace.Get(req1, GetOptions.GetAll); _workspace.Get(req2, GetOptions.GetAll); }
- Loop through the workspace and fetch all csproj and sln files
var workingFolder = _workspace.Folders.FirstOrDefault();if (workingFolder == null) return; var files = Directory.GetFiles(workingFolder.LocalItem, "*.csproj", SearchOption.AllDirectories);
- Parse the csproj file to read the target version, output type, projectTypeGuid, Reference properties
var ppList = new List<ProjectProperty>();foreach (var file in files) { var pp = new ProjectProperty { Name = Path.GetFileName(file), ServerPath = _workspace.GetServerItemForLocalItem(file) }; var history = _vcs.QueryHistory(pp.ServerPath, RecursionType.OneLevel).FirstOrDefault(); if (history != null) { pp.LastCheckIn = history.CreationDate.ToString("r"); pp.LastChangedBy = history.OwnerDisplayName; } var doc = XDocument.Load(file); var ns = doc.Root.Name.Namespace; foreach (var propGroup in doc.Descendants(ns + "PropertyGroup")) { var targetFrameworkVersion = propGroup.Descendants(ns + "TargetFrameworkVersion").FirstOrDefault();if (targetFrameworkVersion == null) continue; var frameworkVersion = targetFrameworkVersion.Value; pp.TargetFrameworkVersion = frameworkVersion; var outputType = propGroup.Descendants(ns + "OutputType").FirstOrDefault();if (outputType == null) continue; var oType = outputType.Value; pp.OutputType = oType; var projectTypeGuids = propGroup.Descendants(ns + "ProjectTypeGuids").FirstOrDefault();if (projectTypeGuids == null) continue; var prjTypeGuids = projectTypeGuids.Value; var pType = (from fmw in prjTypeGuids.Split(';') let value = ProjectTypeDictionary.ProjectType.Where(c => c.Key.ToLower() == fmw.ToLower()) select value.Any() ? value.FirstOrDefault().Value : fmw).Aggregate(string.Empty, (current, o) =>string.IsNullOrEmpty (current) ? o : string.Format("{0}, {1}", current, o)); pp.ProjectType = pType; } var inc = string.Empty;foreach (var reference in doc.Descendants(ns + "Reference")) { var include = reference.Attribute("Include");if (include != null) inc = string.IsNullOrEmpty(inc) ? include.Value : string.Format("{0} {1} {2}", inc, Environment.NewLine, include.Value); var requiredTargetFramework = reference.Descendants("RequiredTargetFramework").FirstOrDefault();if (requiredTargetFramework != null) inc = string.Format("{0} {1}", inc, requiredTargetFramework.Value); } pp.Reference = inc; ppList.Add(pp); }
- Convert the projectTypeGuid to Project Name
{"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}", "C#"}, {"{F2A71F9B-5D33-465A-A702-920D77279786}", "F#"}, {"{E6FDF86B-F3D1-11D4-8576-0002A516ECE8}", "J#"}, {"{F184B08F-C81C-45F6-A57F-5ABD9991F28F}", "VB.NET"}, {"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}", "C++"}, {"{349C5851-65DF-11DA-9384-00065B846F21}", "Web Application"}, {"{E24C65DC-7377-472B-9ABA-BC803B73C61A}", "Web Site"}, {"{F135691A-BF7E-435D-8960-F99683D2D49C}", "Distributed System"}, {"{3D9AD99F-2412-4246-B90B-4EAA41C64699}", "Windows Communication Foundation (WCF)"}, {"{60dc8134-eba5-43b8-bcc9-bb4bc16c2548}", "Windows Presentation Foundation (WPF)"}, {"{C252FEB5-A946-4202-B1D4-9916A0590387}", "Visual Database Tools"}, {"{A9ACE9BB-CECE-4E62-9AA4-C7E7C5BD2124}", "Database"}, {"{4F174C21-8C12-11D0-8340-0000F80270F8}","Database (other project types)"}, {"{3AC096D0-A1C2-E12C-1390-A8335801FDAB}","Test"}, {"{20D4826A-C6FA-45DB-90F4-C717570B9F32}","Legacy (2003) Smart Device (C#)"}, {"{CB4CE8C6-1BDB-4DC7-A4D3-65A1999772F8}","Legacy (2003) Smart Device (VB.NET)"}, {"{4D628B5B-2FBC-4AA6-8C16-197242AEB884}","Smart Device (C#)"}, {"{68B1623D-7FB9-47D8-8664-7ECEA3297D4F}","Smart Device (VB.NET)"}, {"{32F31D43-81CC-4C15-9DE6-3FC5453562B6}","Workflow Foundation"}, {"{14822709-B5A1-4724-98CA-57A101D1B079}","Workflow (C#)"}, {"{D59BE175-2ED0-4C54-BE3D-CDAA9F3214C8}","Workflow (VB.NET)"}, {"{06A35CCD-C46D-44D5-987B-CF40FF872267}","Deployment Merge Module"}, {"{3EA9E505-35AC-4774-B492-AD1749C4943A}","Deployment Cab"}, {"{978C614F-708E-4E1A-B201-565925725DBA}","Deployment Setup"}, {"{AB322303-2255-48EF-A496-5904EB18DA55}","Deployment Smart Device Cab"}, {"{A860303F-1F3F-4691-B57E-529FC101A107}","Visual Studio Tools for Applications (VSTA)"}, {"{BAA0C2D2-18E2-41B9-852F-F413020CAA33}","Visual Studio Tools for Office (VSTO)"}, {"{F8810EC1-6754-47FC-A15F-DFABD2E3FA90}","SharePoint Workflow"}, {"{EC05E597-79D4-47f3-ADA0-324C4F7C7484}","SharePoint (VB.NET)"}, {"{593B0543-81F6-4436-BA1E-4747859CAAE2}","SharePoint (C#)"}, {"{6D335F3A-9D43-41b4-9D22-F6F17C4BE596}","XNA (Windows)"}, {"{2DF5C3F4-5A5F-47a9-8E94-23B4456F55E2}","XNA (XBox)"}, {"{D399B71A-8929-442a-A9AC-8BEC78BB2433}","XNA (Zune)"}, {"{A1591282-1198-4647-A2B1-27E5FF5F6F3B}","Silverlight"}, {"{F85E285D-A4E0-4152-9332-AB1D724D3325}","Model-View-Controller v2 (MVC2)"}, {"{E53F8FEA-EAE0-44A6-8774-FFD645390401}","Model-View-Controller v3 (MVC3)"}, {"{E3E379DF-F4C6-4180-9B81-6769533ABE47}","Model-View-Controller v4 (MVC4)"}, {"{BC8A1FFA-BEE3-4634-8014-F334798102B3}","Windows Store Apps (Metro Apps)"}
- Use the TFS API to get the last check in date and last check in owner for the file
var history = _vcs.QueryHistory(pp.ServerPath, RecursionType.OneLevel).FirstOrDefault();if (history != null) { pp.LastCheckIn = history.CreationDate.ToString("r"); pp.LastChangedBy = history.OwnerDisplayName; }
- Present the results on the UI
dgProjectDetails.ItemsSource = dt.DefaultView;
dgProjectDetails.AutoGenerateColumns = true;
Download
Download a working demo solution from here.
If you have any feedback please feel free to leave a comment. Thank you for taking the time out and reading this blog post. If you enjoyed the post, remember to subscribe to http://feeds.feedburner.com/TarunArora. Stay tuned!
Enjoy!