TFS2010 Object Model - Introduzione, server, collezioni e progetti
Scritto da
Massimo Bonanni
il
mercoledì 9 febbraio 2011
Linguaggio:
•
Framework:
•
Livello: 200
Download sorgenti
Download pdf
Quest'articolo è il primo di una serie dedicata all'object model
di TFS 2010 e al suo utilizzo per interagire con la piattaforma
Visual Studio ALM.
In particolare, in quest' articolo affronteremo l'architettura
dell'object model e i concetti principali per interagire con TFS,
ovvero, connessione al server, recupero delle collections e dei
progetti.
Architettura della piattaforma (a cura di
Matteo Emili)
Abbiamo a disposizione due object model ben distinti:
- Client Object Model : permette di realizzare dei nostri client
che interagiscono con la piattaforma TFS. E' l'argomento di questa
serie di articoli;
- Server Object Model: permette di estendere le funzionalità
della piattaforma TFS. Non ci occuperemo di questo aspetto in
questa serie di articoli.

Come possiamo vedere dalla figura, Team Foundation Server espone
le sue funzionalità attraverso dei Web Services che, in linea
teorica possiamo tranquillamente richiamare dal nostro codice.
Questi Web Service sono:
- Classification Service, che permette l'accesso
a tutte le informazioni dei progetti ed alla sezione di Version
Control;
- Eventing Service, che espone la possibilitò di
comunicare in modo bidirezionale (es.: Team Build);
- Linking Service, è il servizio che ha in
carico la gestione delle dipendenze di ogni artefatto (che siano
file nel Version Control o Work Item) del Team Foundation
Server;
- Security Service, che implementa il security
model e genera automaticamente le ACL per ogni componente, sia out
of the box che estensione:
- Registration Service, l'entry point
dell'estendibilità mediante servizi. Ogni servizio deve essere
registrato nel Registration Service, e permette anche il discovery
di nuovi servizi che vengono a loro volta pubblicati;
Richiamare direttamente i web service di TFS, però comporta una
serie di problematiche: si tratta di web service non documentati,
quindi ogni operazione viene fatta "alla cieca" e senza alcun tipo
di supporto, ma soprattutto saremmo legati alla struttura dei web
service stessi e, eventuali cambiamenti in questa, ci
costringerebbero a un cambiamento sostanziale del nostro codice e
ad una nuova analisi dei web services nella nuova versione.
L'Object Model Client, invece, si interpone fra i web service e la
nostra applicazione, fornendo una serie di classi che fungono da
wrapper per le chiamate, è aggiornato e distribuito direttamente da
Microsoft, corredato da documentazione (sia nell'IDE che su MSDN)
e, in definitiva, ci permette di scrivere codice in modo più
semplice, funzionale e manutenibile. Entrando in dettaglio alle
funzionalità esposte dall'Application Tier, troviamo i seguenti
blocchi costitutivi:

I web services di TFS espongono le funzionalità relative ai
progetti contenuti nelle collections ed in particolare, quelli di
cui ci occuperemo in dettaglio sono:
- Work Item Tracking
- Version Control
- Build Service
I Servizi di framework (Team Foundation Framework Service)
forniscono tutta una serie di funzionalità trasversali alla
piattaforma come ad esempio la gestione delle ACL.
Di questi servizi non ci occuperemo esplicitamente anche se,
ovviamente, li utilizzeremo.
Le DLL principali sono tre:
- Microsoft.TeamFoundation.dll, che contiene
tutti i componenti base (classi, interfacce, ecc.) che sono
utilizzati dagli altri assembly
- Microsoft.TeamFoundation.Client.dll, che
contiene tutte le classi di connettività ed autenticazione
-
Microsoft.TeamFoundation.WorkItemTracking.Client.dll,
che dà la possibilità di navigare e modificare il TFS Work Item
Store
-
Microsoft.TeamFoundation.VersionControl.Client.dll,
che dà la possibilità di gestire il versioning dei sorgenti;
- Microsoft.TeamFoundation.Build.Client.dll, che
dà la possibilità di interagire con il processo di build
automatizzato delle nostre applicazioni.
Server, collezioni e progetti
La classe fondamentale per l'accesso al TFS è la
TFSConnection contenuta all'interno del namespace
Microsoft.TeamFoundation.Client (assembly
Microsoft.TeamFoundation.Client.dll).
TFSConnection è una classe astratta (MustInherit) e rappresenta la
connessione del client verso il server che ospita TFS.
Da questa classe derivano due classi concrete che possiamo
sfruttare per connetterci al server e recuperare, ad esempio,
l'insieme delle collezioni presenti.

Le due classi TFSConfigurationServer e TFSTeamProjectCollection
consentono, rispettivamente, di accedere ai servizi e le proprietà
del server e di una singola project collection.
Entrambe le classi, derivando da TFSConnection, mettono a
disposizione la possibilità di autenticarsi e di recuperare il
riferimento a un servizio (vedremo più avanti nella serie cosa
significa ciò).
Nell'esempio proposto in questo articolo vogliamo elencare
le collection presenti all'interno del server e i progetti
contenuti in queste ultime.
Per fare ciò è necessario utilizzare la classe
TFSConfigurationServer che mette a disposizione la possibilità di
recuperare l'elenco delle collections.
Possiamo ottenere un'istanza di TFSConfigurationServer in due
modi:
- Utilizzare il costruttore della classe
TFSConfigurationServer;
- Utilizzare la classe TFSConfigurationServerFactory che, come
tutte le classi factory, si occupa di costruire in maniera
opportuna l'oggetto TFSConfigurationServer.
La sostanziale differenza tra le due possibilità è che se
utilizziamo il costruttore otteniamo diverse istanze della classe
TFSConfigurationServer mentre se utilizziamo la classe Factory, il
framework, restituirà sempre la stessa istanza.
Nel nostro caso utilizzeremo la seconda strada.
Tra i tanti overload del metodo GetConfigurationServer della
classe statica TFSConfigurationServerFactory utilizzeremo il:
Public Shared Function GetConfigurationServer (uri As Uri,
fallbackCredentialsProvider As ICredentialsProvider) As TfsConfigurationServer
dove uri contiene l'indirizzo del server TFS (in maniera analoga
a quanto facciamo nel Team Explorer in Visual Studio quando
vogliamo connetterci a TFS), mentre fallbackCredentialProvider è un
qualunque oggetto che implementa l'interfaccia ICredentialProvider
in grado di "fornire" le credenziali di accesso nel caso in cui
servano.
Nei nostri primi esempi utilizzeremo come fornitore di
credenziali di accesso la classe UICredentialProvider (del
namespace Microsoft.TeamFoundation.Client) che richiede le
credenziali con un prompt a video.
Vedremo, in seguito come creare un nostro fornitore di
credenziali per tutti quegli scenari in cui non è possibile o non è
necessario richiede le credenziali all'utente.
A questo punto, cominciamo a scrivere la nostra classe di
servizio per l'accesso a TFS.
Imports Microsoft.TeamFoundation.Client
Imports System.Net
Imports System.Diagnostics.Contracts
Public Class TFSService
Implements IDisposable
Public Sub New(ByVal serverUrl As String)
Me.ServerURL = serverUrl
End Sub
Public Sub New()
End Sub
Protected Property TFSInstance() As TfsConfigurationServer
Public Property ServerURL As String
Public Overridable Function Login() As Boolean
Dim retval = False
If TFSInstance IsNot Nothing Then
TFSInstance.Dispose()
TFSInstance = Nothing
End If
Try
TFSInstance = TfsConfigurationServerFactory.GetConfigurationServer(New Uri(Me.ServerURL), _
New UICredentialsProvider())
TFSInstance.Authenticate()
retval = True
Catch ex As System.Exception
retval = False
TFSInstance = Nothing
End Try
Return retval
End Function
#Region "IDisposable Support"
.
.
.
#End Region
End Class
Tralasciamo la sezione contenente l'implementazione
dell'interfaccia IDisposable, necessaria per rilasciare
correttamente l'eventuale istanza di TFSConfigurationServer tramite
la chiamata al metodo Dispose(), ma non interessante in questo
contesto.
Nel metodo Login utilizziamo la factory per creare l'istanza di
TFSConfigurationServer (sfruttando il fornitore di credenziali che
visualizza il popup all'utente) e richiamiamo il metodo
Authenticate per eseguire fisicamente l'autenticazione.
In realtà non sarebbe necessario eseguire il metodo Authenticate
perché nel momento in cui si utilizza l'istanza di
TFSConfigurationServer per recuperare le informazioni dal server,
la stessa classe provvede ad eseguire automaticamente il tentativo
di autenticazione utilizzando l'ICredentialProvider in caso di
fallimento.
Nel nostro metodo di Login è stato utilizzato semplicemente per
forzare l'autenticazione e verificare che i dati di accesso siano
corretti.
Nello stesso metodo di Login intercettiamo genericamente
l'eventuale eccezione generata dal tentativo di connessione e
restituiamo False al chiamante.
In uno scenario reale, la risposta al chiamante dovrebbe essere più
dettagliata per permettere di capire cosa realmente è successo (il
server non esiste, l'utente non ha i privilegi, etc., etc.).
Per implementare questo, l'object model di TFS mette a disposizione
una serie di eccezioni che troviamo nel namespace
Microsoft.TeamFoundation.
Supponendo, quindi, di essere in grado di verificare che il
login funzioni, implementiamo il metodo che consente di recuperare
l'elenco delle project collections presenti nel server.
La classe TFSConnection espone una proprietà chiamata CatalogNode
(dichiarata MustOverride e di sola lettura) che restituisce un
oggetto di tipo CatalogNode che sostanzialmente rappresenta il
catalogo delle risorse presenti nel server puntato dalla
TFSConnection.
La classe CatalogNode espone il metodo QueryChild la cui funzione è
quella di eseguire una query sul catalogo alla ricerca di
particolari risorse. Il metodo ha due overloads e utilizzeremo
quello con la firma:
Public Function QueryChildren ( _
resourceTypeFilters As IEnumerable(Of Guid), _
recurse As Boolean, _
queryOptions As CatalogQueryOptions) As ReadOnlyCollection(Of CatalogNode)
Gli argomenti del metodo hanno la seguente funzione:
- resourceTypeFilters : è una enumerazione di
GUID. Vengono utilizzati i GUID perché tutte le risorse in TFS sono
recuperabili tramite questo tipo di identificatore. Anche i tipi di
risorse sono recuperabili tramite GUID. La classe
CatalogResourceTypes prevede dei campi statici che restituiscono i
GUID delle risorse di TFS (per maggiori info
http://msdn.microsoft.com/en-us/library/microsoft.teamfoundation.framework.common.catalogresourcetypes.aspx);
- recurse: permette di eseguire la query in
maniera ricorsiva. Nel caso di ricerca di project collection
all'interno di un server TFS non ha senso in quanto una project
collection non contiene altre project collection;
- queryOptions: permette di definire le opzioni
della query.
Il risultato del metodo è una collezione in sola lettura di
CatalogNode che possiamo enumerare per recuperare le informazioni
che ci servono.
Nel nostro caso potremmo scrivere il seguente metodo:
Public Function GetCollections() As IEnumerable(Of ProjectCollection)
Dim retList As IEnumerable(Of ProjectCollection) = Nothing
If Me.TFSInstance IsNot Nothing Then
Try
Dim collections = Me.TFSInstance.CatalogNode().QueryChildren({CatalogResourceTypes.ProjectCollection},
False,
CatalogQueryOptions.None)
retList = From o In collections
Select New ProjectCollection() With {.Identifier = o.Resource.Identifier,
.Name = o.Resource.DisplayName,
.Description = o.Resource.Description}
Catch ex As Exception
Throw
End Try
End If
Return retList
End Function
La classe ProjectCollection contiene i dati della collection che
ci servono (in pratica l'identificativo, il nome e la
descrizione).
Lo stesso meccanismo utilizzato per recuperare l'elenco delle
collection è applicabile per il recupero dell'elenco dei progetti
all'interno di una collection.
Possiamo, infatti, eseguire una query sul server alla ricerca di
oggetti di tipo "TeamProject", applicando una procedura ricorsiva e
recuperando anche i nodi padre in modo da poter, in seguito,
filtrare il risultato in base all'identificativo della collection
ricercata.
Possiamo, in definitiva, scrivere:
Public Function GetProjects(ByVal collectionGuid As Guid) As IEnumerable(Of Project)
Dim retList As IEnumerable(Of Project) = Nothing
If Me.TFSInstance IsNot Nothing Then
Try
Dim projects = Me.TFSInstance.CatalogNode().QueryChildren({CatalogResourceTypes.TeamProject},
True,
CatalogQueryOptions.IncludeParents)
retList = From o In projects
Where o.ParentNode.Resource.Identifier.Equals(collectionGuid)
Select New Project() With {.Identifier = o.Resource.Identifier,
.Name = o.Resource.DisplayName,
.Description = o.Resource.Description}
Catch ex As Exception
Throw
End Try
End If
Return retList
End Function
In realtà, il codice visto in precedenza non è l'unico modo
possibile per recuperare I progetti di una collection.
Possiamo, infatti, recuperare preventivamente la collezione
utilizzando il metodo GetCollection() della classe
TfsConfigurationServer, quindi, eseguire una query sulla proprietà
CatalogeNode della collection stessa per recuperare le informazioni
dei progetti:
Public Function GetProjects2(ByVal collInstanceId As Guid) As IEnumerable(Of Project)
Dim retList As IEnumerable(Of Project) = Nothing
If Me.TFSInstance IsNot Nothing Then
Try
Dim collection = Me.TFSInstance.GetTeamProjectCollection(collInstanceId)
If collection IsNot Nothing Then
Dim projects = collection.CatalogNode().QueryChildren({CatalogResourceTypes.TeamProject},
False,
CatalogQueryOptions.None)
retList = From o In projects
Select New Project() With {.Identifier = o.Resource.Identifier,
.Name = o.Resource.DisplayName,
.Description = o.Resource.Description}
End If
Catch ex As Exception
Throw
End Try
End If
Return retList
End Function
Alla fine dell'articolo siamo in grado di realizzare
un'applicazione che elenca, dato un server TFS, le sue collezioni
e, fissata una collezione, i progetti in essa contenuti (il codice
completo dell'applicazione è contanuto nei sorgenti in
allegato).

Per concludere vediamo come implementare un CredentialsProvider
da poter utilizzare dove non è possibile visualizzare un prompt per
richiedere le credenziali all'utente (ad esempio un web service o
un servizio di windows).
Un credential provider altro non è che una classe che implementa
l'interfaccia ICredentialsProvider ed in particolari i due
metodi:
- GetCredentials : viene richiamato quando la
connessione a TFS ha necessità di avere un oggetto che implementa
ICredentials (ad esempio un oggetto di tipo NetworkCredentials) per
autenticare l'utente;
- NotifyCredentialAuthenticated : viene
richiamato dal framework per notificare al provider che le
credenziali sono correttamente verificate.
Una semplice classe che utilizza NetworkCredentials per
autenticare l'utente è la seguente:
Imports Microsoft.TeamFoundation.Client
Imports System.Net
Friend Class CredentialProvider
Implements ICredentialsProvider
Public Sub New(ByVal credentials As ICredentials)
If credentials Is Nothing Then
Throw New ArgumentNullException("credentials")
End If
Me._Credentials = credentials
End Sub
Private _Credentials As ICredentials
Public Function GetCredentials(ByVal uri As System.Uri, _
ByVal failedCredentials As System.Net.ICredentials) As System.Net.ICredentials _
Implements ICredentialsProvider.GetCredentials
Return _Credentials
End Function
Public Sub NotifyCredentialsAuthenticated(ByVal uri As System.Uri) _
Implements ICredentialsProvider.NotifyCredentialsAuthenticated
End Sub
End Class
Riferimenti
[1] Team Foundation Server Architecture:
http://msdn.microsoft.com/en-us/library/ms252473.aspx
[2] Extending Team Foundation: http://msdn.microsoft.com/en-us/library/bb130146.aspx
[3] Team Foundation Server 2010 SDK: http://code.msdn.microsoft.com/TfsSdk
[4] Team Foundation Server Team Blog: http://blogs.msdn.com/b/team_foundation/
Tags: TFS,Team Foundation Server