TFS API: How to choose between TeamFoundationServerFactory.GetServer() and the TeamFoundationServer constructor

Occasionally the question of whether to use the TeamFoundationServerFactory.GetServer() method or the TeamFoundationServer constructor comes up, and someone recently asked this question on an internal list.  The answer depends on the needs of the application that you are writing.  In this post, I'll discuss the main differences between the two.

If you have an application where different parts of the code will need to access the same TeamFoundationServer object (say you need to hook up event handlers to the same VersionControlServer object that the rest of the code is using) but only have the name/URL of the server, then the TeamFoundationServerFactory is the correct choice. The TeamFoundationServerFactory caches the TeamFoundationServer objects it creates and always returns a cached TeamFoundationServer object if one matching the request exists. Those cached objects are indexed only by server URL, which is why there is no way to specify the credentials in the GetServer() method. Every time you request the TeamFoundationServer object for http://yourserver:8080, for example, via TeamFoundationServerFactory.GetServer(), you will get the same exact instance, and thus no chance to connect to the same server with different credentials.

The Team Explorer needs this type of behavior and is why TeamFoundationServerFactory exists. The different parts of the code shouldn't pop up additional login dialogs and yet the code wouldn't know how to retrieve the existing TFS object from a cache whose key included the credentials without first asking the user what credentials to use (there are also complications in dealing with CredentialCache objects; you can read more about CredentialCache objects in the post, Authentication in web services with HttpWebRequest).

However, if you want to connect to the same Team Foundation Server with different credentials, you must use the constructor. For example, you connect to server A as users X and Y.

TeamFoundationServer tfsX = 
new TeamFoundationServer("http://serverA:8080",
new NetworkCredential(userX, passwordX, domain));
TeamFoundationServer tfsY =
new TeamFoundationServer("http://serverA:8080",
new NetworkCredential(userY, passwordY, domain));

The command line, tf.exe, is an example of an application that can do that via command files containing commands with /login options specifying different users.  For example, you could execute the following by saving the commands in a file called cmd.tfc and running tf @cmd.tfc.  These two commands execute within the context of one running instance of tf.exe, which means tf.exe must use the constructor.

edit foo.cs /login:userX,passwordX
perm $/blah/foo.cs /deny:read /login:userY,passwordY

If you don't have either of these requirements and just need a TeamFoundationServer object to do something, you could choose either.  You'll find examples of both in this collection of code that uses the API.  Personally, I tend to use the constructor for utility apps and examples, but there's no profound reason behind it.

If you need to prompt the user for credentials to connect to the server, see the post, How to get the login dialog when using the Team Foundation Server API.  The example uses the constructor, but you can also pass the credential provider to the TeamFoundationServerFactory.GetServer() method.

tags: , ,

Comments (8)

  1. curedone says:


    Could you clarify something for me? Let’s say I use TeamFoundationServerFactory GetServer method several time with same URL. Does the internal caching result in "same" object returned?

    For example, if I retrieve VersionControlServer using GetService method of TeamFoundationServer (first instance), and then hook up on VersionControlServer event, does that mean that VersionControlServer from second instance of TeamFoundationServer will have that event hook already in place?

    Here is some pseudo code (wont compile :):

    // first place

    tfs1 = TeamFoundationServerFactory.GetServer(url);

    vc1 = tfs1.GetService();

    vc1.NewPendingChange += event1;

    // second place

    tfs2 = TeamFoundationServerFactory.GetServer(url);

    vc2 = tfs2.GetService();

    vc2.NewPendingChange += event1;


  2. buckh says:

    Yes, that is correct.  So long as the url is the same, the same TFS object is returned, and the event handler shown in your code will already have been added (vc2 and vc1 reference the same VersionControlServer object).

    Note that it’s literally requires the URL to match to get the same object.  If you provide an URL using the name and then specify an URL using the IP address, you won’t get the same object.


  3. D’Arcy Lussier on Storyboarding for Team System. Rob Caron talks about it as well.

    Tan on Customizing…

  4. Naren's Blog says:

    For those who never used work item tracking object model and want to start: I saw a nice post that…

  5. Ravi Terala says:

    I recommand the obtaining services from TFS OM for all services except the WorkItemStore. It is not thread safe, where as all other services you obtain from TFS OM are. To work around this issue, create a new WorkItemStore object and pass the credentials that you get from the TFS OM.

  6. buckh says:

    Ravi, while that may be true (I don’t remember — there were changes made to address at least some of the issues), it only matters when making calls from multiple threads.  Folks writing single-threaded apps certainly don’t have to worry about it and can get the WorkItemStore object via the usual TeamFoundationServer.GetService() method.


  7. J Sawyer says:

    Don’t call Dispose() on the TeamFoundationServer object that you get from the TeamFoundationServerFactory. Because it’s the same instance for each URL, this will result in the cached instance being disposed and causing some mysterious errors; any call to GetService() will raise a NullReferenceException. There don’t seem to be any checks in the TFS object to see if it has been disposed so it won’t throw an ObjectDisposedException.

  8. buckh says:

    J Sawyer, yes, that is a good point.


Skip to main content