Determining the TFS Server Version Using Client APIs

In today’s software world supporting backward and forward compatibility between different versions of clients and servers for a given product is usually both a headache and a hard-requirement from the customer.  When writing a client application that supports talking to different versions of a server one of the key problems to solve is figuring out what version of the server the client is talking to so that the client knows what functionality the server offers.  This post will hopefully help the author of a client application that talks to a Team Foundation Server determine what version the server is, and most importantly what services this version of the server offers.

Disclaimer: The code presented in this blog post assumes you are using the RTM version of TFS. Some of these APIs were refactored between TFS 2010 Beta 2 and TFS 2010 RTM. Please see the section at the end of the post to see how the new API names map to those in the Beta 1 and Beta 2 versions of TFS 2010.

If you are new to the TFS 2010 OM you may want to start with this post which introduces the TfsConnection, TfsConfigurationServer and TfsTeamProjectCollection objects.

In order to use the code from the examples in this post you will need the following include statements:

 using System;
using System.Collections.Generic;
using Microsoft.TeamFoundation;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Framework.Client;
using Microsoft.TeamFoundation.Server;

 What Services Does this Instance of TFS Offer?

Included in the release of Microsoft Team Foundation Server 2010 there are a number of new services that exist that provide a great deal of new functionality.  Unfortunately, for a client application developer, in order to take advantage of some of these new features the client must first determine if the given TFS server supports these new features.

It seems that one way to accomplish this would be to maintain a list of what features are shipped in each version of TFS, determine which version of TFS is being talked to and then make assumptions about what services can be called.  Firstly, this method seems a bit clunky and has a lot of overhead.  Where are you going to find a list of all of the services that each version of TFS offers?  How are you going to represent these in your codebase?  And most importantly, how will your support forward compatibility with future TFS versions if you don’t know what services they will or will not offer?

Also, what about server-side third party add-ins for TFS?  How will you be able to tell if any of these exist if all you are doing is looking at some type of version stamp for the given server? 

The answer is that you won’t be able to do any of these things effectively by following this approach.  Instead of memorizing what services are available in each version of TFS it seems that what we really should be doing is asking this instance of TFS what services it offers.

So how do I ask a given instance of TFS what services it offers?

Fortunately, there is a TFS service for that!  Well, that is not its sole purpose of the service but the Team Foundation Server Location Service has the ability to tell the client what services are available from a given instance of TFS.  This is a new service that has been introduced in TFS 2010.

Wait, if it is a new service in TFS 2010 then how do I know if I can use it without checking to see if I am talking to a TFS 2010 server first?

Great question, I am glad to see you have been paying attention.  Fortunately, a few of the new services that we have introduced in TFS 2010 have backward compatibility built-in to parts of them and the location service is one of these services.  That means, whether you are talking to the 2005, 2008 or 2010 version of TFS you will be able to use certain functions from the location service.

Let’s start by seeing a code example of how you can retrieve the location service to start using it:

 TfsTeamProjectCollection teamProjectCollection = 
        new TfsTeamProjectCollection(new Uri("https://your-server:8080/tfs/Collection0"));

ILocationService locationService = teamProjectCollection.GetService<ILocationService>();

Note, the location service is also available off of a TfsConfigurationServer object and can be used the same way we will use it in the following examples. 

Before we dig in on how you can use this object to determine if a given service is present we first need to get some background on what exactly the location service does.  I am planning a larger post at some point to provide an in-depth look at the functionality of the location service so I will keep this short.  For the purpose of this discussion, you can think of the location service as a means for the registration and discoverability of the services offered within a TFS instance.  In the location service, a service is defined by an object called a ServiceDefinition. 

The ServiceDefinition object contains many properties that convey the different attributes of a given service but for right now we will only worry about the ServiceType and Identifier properties.  Much like a .NET interface a service defines a collection of methods that serve as a contract between the user of the service and the implementer of the service.  This collection of methods is given a name, just as a .NET interface is given a name, and that name is what populates the ServiceType field.  Now, just as a .NET interface can have many different classes that implement it, the same is true for our ServiceType.  This means that multiple ServiceDefinitions can have the same ServiceType.  However, each different implementation of a given ServiceType will have its own unique Identifier (in the form of a GUID).

With that knowledge, hopefully the next sections will make sense. 

When coding it is generally useful to know if a specific service exists.  Let’s say that you want to take advantage of the new Team Foundation Server Security Service that has been introduced in TFS 2010 but you also want to fail gracefully if you are talking to a version of the server that doesn’t offer the new security service.  Moreover, let’s also assume that you want to work with the specific security service that TFS ships with.  In order to accomplish this your code could look like this:

 if (null != locationService.LocationForCurrentConnection(ServiceInterfaces.SecurityService, 
                                                         FrameworkServiceIdentifiers.CollectionSecurity)) 
{ 
    DoSecurityServiceWork(); 
}
else
{
    ShowMessageThatSecurityServiceIsNotAvailableForThisServer();
}

This code is asking the location service if there is a known location for a service that fulfills the security service contract and has the well-known identifier that represents the TFS implementation of the security service.  If the location service returns an object that means the service is registered and if it returns null then it means that this service does not exist on the current server.  Each of the TFS Services has a well known ServiceType and a well-known Identifier that uniquely define and identify it.

Cool, so how expensive is it to ask the location service if a service is defined? Does it make a server call each time?

The first time a call is made to the location service for a given host it will query that host for all of the services defined on it.  All of those services are then cached on the client and each subsequent call uses that cache to determine if the service is present or not.  Furthermore, this cache is persisted to disk so the next time this client application starts up it will not have to query the services again.  That means that unless we are talking about the first call to this location service then each call to look up a service is very inexpensive.

But what if I really, really just need to know what version of the server I am talking to?

Unfortunately there is not some uniform method that you can call that will simply tell you “You are communicating with version X of TFS”.  In order to determine what version of the server you are talking to we are going to use the principals about querying for services above along with some knowledge about what services were available in each release.

As mentioned above, the security service is new in TFS 2010.  We also know that version 2 of the group security service was introduced in TFS 2008.  With that information, we can use the following algorithm to determine which version of the server we are talking to:

 if (null != locationService.LocationForCurrentConnection(ServiceInterfaces.SecurityService, 
                                                         FrameworkServiceIdentifiers.CollectionSecurity))
{
    //We are talking to TFS 2010
}
else if (null != locationService.LocationForCurrentConnection("GroupSecurity2", 
                                                              IntegrationServiceIdentifiers.GroupSecurity2))
{
    //We are talking to TFS 2008
}
else
{
    //We are talking to TFS 2005
}

Since we are talking about compatibility here what if I want to update my 2008 clients to be aware of what server version they are talking to. I am guessing I cannot use the location service, correct?

Correct, the location service isn’t available in the TFS 2008 Client OM.  However, you can still accomplish this feat using the Team Foundation Server Registration Service – though the solution will be a bit more terse.  The registration service is basically a collection of objects called RegistrationEntry.  In TFS 2010 we added a new RegistrationEntry for the value “Framework”.  We will use this along with our other knowledge about version 2 of the group security service to build the following algorithm:

 TeamFoundationServer server = new TeamFoundationServer("https://your-server:8080/");
IRegistration registrationService = (IRegistration)server.GetService(typeof(IRegistration));

RegistrationEntry[] frameworkEntries = registrationService.GetRegistrationEntries("Framework");
if (frameworkEntries.Length > 0)
{
    //We are talking to TFS 2010
}
else
{
    RegistrationEntry[] vstfsEntries = registrationService.GetRegistrationEntries("vstfs");
    if (vstfsEntries.Length != 1)
    {
        //We must be talking to an unknown version of TFS
    }
    else
    {
        Boolean groupSecurity2Found = false;
        foreach (ServiceInterface serviceInterface in vstfsEntries[0].ServiceInterfaces)
        {
            if (serviceInterface.Name.Equals("GroupSecurity2", StringComparison.OrdinalIgnoreCase))
            {
                groupSecurity2Found = true;
            }
        }

        if (groupSecurity2Found)
        {
            //We are talking to TFS 2008
        }
        else
        {
            //We are talking to TFS 2005
        }
    }
}

Hopefully that should be enough to get you started with using the location service to determine what services are available from the TFS Instance you are talking to.  Feel free to ask questions in the comments or contact me via the contact tab.

If you are using TFS 2010 Beta 1 or Beta 2 then everything you just read is still true but some of the APIs have different names. Here are the mappings that you will need to make this work in one of the betas:

TfsTeamProjectCollection is equivalent to TeamFoundationServer.  For more information on this see Grant Holliday's blog post.

Also, in TFS 2010 Beta 2 the function LocationForCurrentConnection was named FindServiceLocation.