The source control solution known as Team Foundation Server truly lives up to its name, as it is a tool that provides a foundation?that allows (and even requires) the intrepid developer to build upon it, so that the complex source control and software management needs of their organization can be met.

The extensibility of Team Foundation is a double-edged sword; although a great deal of things can be accomplished with it, the developer will quickly run into a pretty steep learning curve in regards to how to go about building upon TFS in order to achieve their goals.

There exists an extensive amount of possible programmability with TFS, as one can easily deduce simply by disassembling any of the provided TFS assemblies. Documentation regarding how to go about using these assemblies is scarce on the MSDN, however. To address this, I’m providing this write up as the first in a series of articles regarding how to utilize the Team Foundation API. As version control is the first thing that comes to my mind when I think of the words “Team Foundation”, version control shall be the first topic we’ll?explore.

Before diving into the beast’s maw with version control specifically, let’s look at the object that is at the core of everything dealing with TFS.

Also, before we start, there is not a huge deal of documentation on this, as I have previously mentioned. Therefore, almost all of the following information I’m about to present to you was made possible by disassembling the assemblies. Errors may exist, please point them out.

The first object you’ll commonly encounter is the, aptly named, TeamFoundationServer object. You will find this object in the Microsoft.TeamFoundation.Client assembly. This object is basically used for three things before moving on to something else, namely:

1) Defining your TFS address
2) Getting authenticated
3) Latching on to the core technologies of TF (i.e. VersionControl and Build)

Initializing this object is necessary for a multitude of things; specifically, access to the GetService method which will allow you to tap into the other areas of Team Foundation (such as VersionControl and Build).

You can initialize this core object either by direct initialization:

TeamFoundationServer tfs = new TeamFoundationServer(“TFS_Server_Name”);

…where “TFS_Server_Name” is the machine name running TFS…

…or you can initialize the object via its class factory:

TeamFoundationServer tfs = TeamFoundationServerFactory.GetServer(“TFS_Server_Name”);

//..or, you may use the GetServer(String, ICredentialsProvider) overload.

Using the first overload shown above in the TeamFoundationServerFactory snippet results in “GetServer(String, ICredentialsProvider)” being called with a null value being passed in as the second parameter. While this may first have you believe that this results in a non-authenticated session, the optional parameters that can be passed in this particular GetServer overload are actually used as fallback credentials. They are only used if there is a failure in acquiring credentials from an internal class called UIHost.

/**In GetServer(String, ICredentialsProvider) **/
ICredentialsProvider credentialsProvider = UIHost.CredentialsProvider;
if (credentialsProvider == null)
{
credentialsProvider = fallbackCredentialsProvider;
}
if ((server == null) || server.Disposed)
{
server = new TeamFoundationServer(name, CredentialCache.DefaultCredentials,
credentialsProvider);
m_serverCache[key] = server;
return server;
}

The important thing to note here is that you shouldn’t expect your credentials to be used when calling the GetService factory method unless Team Foundation is subsequently unable to get your credentials from the UIHost class.

Except for specific situations, you should use the class factory to get a new TeamFoundationServer instance. The reason why I suggest this is because the factory maintains a cache of TeamFoundationServer instances and its use will result in the avoidance of the expensive process of actually initializing a new TeamFoundationServer instance.

If you are (cool and) accessing TFS using a multi-threaded application, and thus perhaps requiring the creation of multiple TFS instances, you may not be as pleased with the factory class as you would expect. This is due to the fact that the “hit-or-miss cache retrieval” parts of the GetServer(…) class, as well as the TFS initialization (in case of failure) part, are in a blocking, critical section of code. Therefore, if you are initializing multiple servers at once that you know are not cached, you will want to do an outright initialization using the TeamFoundationServer constuctor (as that can be threaded), as opposed to using TeamFoundationServerFactory.GetServer(…) class.

Well, now that I’ve spent a bit too much time on that little nugget of fun, let’s hit up the version control side of things. The object of interest here is the VersionControlServer, which implements the ITeamFoundationServerObject interface, and it is found in the Microsoft.TeamFoundation.VersionControl.Client assembly. The following demonstrates how we can acquire one of these tasty objects:

VersionControlServer versionControl =
(VersionControlServer)tfs.GetService(typeof(VersionControlServer));

The above code demonstrates how we can latch onto one of the multiple TFS technologies via the GetService call.

The VersionControlServer instance we now have allows us to do just about anything we can think of in relation to source control. This includes things such as workspace management, shelving, changeset manipulation, version control item manipulation, and much more.

The VersionControlServer class also offers about 27 events we can subscribe to; this may interest certain developers as this gives us the opportunity to cook up some very interesting apps or utilities. Although many of them seem valuable, the particularly valuable ones are ones like BeforeCheckinPendingChange, which open up many avenues of operation such as record keeping and even security.

Besides subscribing to events, before we can do any usual sort of operation, we need to be operating within a workspace. This can be done easily like so:

Workspace workspace =
versionControl.CreateWorkspace(“WorkspaceName”, versionControl.Authenticateduser);
//Now we need to map the workspace to a local directory
workspace.Map(“$/Server/Path/To/Some/Directory”, @”C:\some\directory”);

The above should be fairly obvious; we’re creating a workspace and mapping it to a local directory, much like how we create a workspace normally.

Note that the version control operations above and the ones that will follow this are deferred to the internal class: Client. Yes, that is Microsoft.TeamFoundation.VersionControl.Client.Client. Just putting that out there if you need to dig to find out why something is acting the way it is…

The Workspace we just created is where a lot of the nitty gritty version control operations are going to happen. To illustrate what we can do with the Workspace, the following code snippets demonstrates a common scenario:

workspace.Get(); //Get the files from TFS.

//Add a new file and do whatever with it…
//…let’s call it NewStuff.cs

string path =
Path.Combine(workspace.Folders[0].LocalItem, “NewStuff.cs”);

workspace.PendAdd(path);
//This will add (pending) the new file…

//Let’s check it in..
int changeset = workspace.CheckIn(workspace.GetPendingChanges(), “Comment”);

Note that the desired workspace folder property may not be at index 0, in fact it may very well not be, as you will have Team Projects default to the first few positions of the Folders array.

If we want to edit or delete the same file, we can use the PendEdit(…) or PendDelete(…) methods respectively.

What if we edited our file, didn’t like what we just did, and want to get an older version? If you are familiar with retrieving a file from the repository within Visual Studio Source Control Explorer, you know that you are presented with several options: Latest, By Changeset, By Date, etc.

By using the VersionSpec class, we can define how we want to specify the version of an item. The VersionSpec class itself is abstract, but five classes inherit it:
ChangesetVersionSpec, DateVersionSpec, LabelVersionSpec, LatestVersionSpec, and WorkspaceVersionSpec.

Using one of these and a combination of other methods, we can get a previous version of our file. The most obvious way to get a previous changeset ID for an item is to view its history. That would be appropriate in this scenario; however, I need to further analyze exactly how to grep through the history, as all trails seem to end up at a compiler generated private class. For our purposes, then, let us assume that we know the changeset number from some other means. Let’s assume the changeset number is 23.

The following code snippet demonstrates how to get the previous version, using three different ways:

//This gets an object representing the item, useful in many ways…
Item newStuff =
versionControl.GetItem(path, new ChangesetVersionSpec(23));

//This actually downloads the item to the properly mapped directory in the workspace…
versionControl.DownloadFile(
Path.Combine(workspace.Folders[0].ServerItem, “NewStuff.cs”),
0,
new ChangesetVersionSpec(23),
“NewStuff.cs” //desired local file name
);

//This performs a more advanced download…
GetStatus status =
workspace.Get(
new string[] {path},
new ChangesetVersionSpec(23),
RecursionType.None,
GetOptions.Overwrite
);

The above will either retrieve an object of the previous-versioned item, or actually download the previous version to your mapped local directory. The GetStatus object that was returned contains important information such as the number of failures, conflicts, etc.

In case you are curious, what the “Get” call boils down to eventually (and I do mean eventually) is a web service request. The web service method name is “Get” (unsurprisingly), with the workspace name, owner name, and request (which is the items desired combined with the recursion type and version spec) being passed as XML elements in the SOAP request. Additionally, there are some optional elements provided, however those don’t need to be looked at now.

This concludes this very brief introduction to the TFS and VersionControl API. Next I’m going to do a basic introduction of the Team Build technology API, and then following that I will do more advanced articles that showcase usage of the TFS API in specialized areas.

Thanks for reading.

Matt Weber

I'm the the Senior Software Architect at Emergingsoft where I lead the software development team. I am also the owner of this website. I enjoy well-designed code, independent thought, and the application of rationality in general. You can reach me at matt@badecho.com.

 Leave a Reply

(required)

(required)

You may use these HTML tags and attributes: <a href="" title="" rel=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

 
© 2012-2013 Matt Weber. All Rights Reserved. Terms of Use.