tech·nic·al·ly agile

TFS Event Handler in .NET 3.5 Part 2 - Handling Team Foundation Server Events

Learn to create a resilient TFS Event Handler in .NET 3.5. This guide covers handling events effectively, enhancing your development workflow.

Published on
9 minute read
Image
https://nkdagility.com/resources/8LCPn1m44qL

I have decided to have a little go at creating a Team Foundation Server Event Handler in .NET 3.5 that is resilient and scaleable. I will be using as many of the features of Team Suit as I can, but bear with me as there are a few things that are new to me.

TFS  Event Handler in .NET 3.5 Articles

  1. TFS Event Handler in .NET 3.5 Part 1 - The Architecture   
  2. TFS Event Handler in .NET 3.5 Part 2 - Handling Team Foundation Server Events 
  3. TFS Event Handler in .NET 3.5 Part 3 - Passing the events over a Windows Communication Foundation MSMQ (Coming soon)
  4. TFS Event Handler in .NET 3.5 Part 4 - Workflow (Coming soon)

Handling Team Foundation Server Events

Because of the lack of support for Windows Communication Foundation  in the Team Edition for Architects I will be replacing all of the auto-generated services with Windows Communication Foundation  services. But as it turns out this messes up everything, so I have started from scratch and I will post all of the code for doing this without the Architect bit.

The first thing that you need is the Contract for Team Foundation Server event handling. This is very specific and only works if used accurately:

 1Imports System.ServiceModel
 2Imports System.Runtime.Serialization
 3Imports Microsoft.TeamFoundation.Server
 4
 5    ''' <summary>
 6    ''' This is the service contract for integrating with the Team Foundation Server notification events.
 7    ''' </summary>
 8    ''' <remarks></remarks>
 9    <ServiceContract(Namespace:="http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03")> _
10    Public Interface INotification
11
12        ''' <summary>
13        ''' The Notify method if fired whenever a subscribed event arrives.
14        ''' </summary>
15        ''' <param name="eventXml">This XML defines the data that was changed on the event.</param>
16        ''' <param name="tfsIdentityXml">This XML identifies the Team Foundation Server the event came from.</param>
17        ''' <param name="SubscriptionInfo">Information about the subscriber</param>
18        ''' <remarks></remarks>
19        <OperationContract( _
20                        Action:="http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03/Notify", _
21                        ReplyAction:="*" _
22                        )> _
23        <XmlSerializerFormat( _
24                        Style:=OperationFormatStyle.Document _
25                        )> _
26        Sub Notify(ByVal eventXml As String, ByVal tfsIdentityXml As String, ByVal SubscriptionInfo As SubscriptionInfo)
27
28    End Interface

This code allows you to capture TFS events and we will be using by reference only. If you have been having trouble handling the events then look know further than here for Windows Communication Foundation  as this has been tried and tested.

The real trick for handling TFS events in Windows Communication Foundation  is that all the events come into the same routine. It would be nice if SubscriptionInfo listed what event it was, but alas it does not. So you may be thinking that you need to parse the XML and find out what type of event it is. Well you can if you want, but I find it easer to have more than one endpoint for the same service. You can then parse the URL for the event type, which is way easier than the XML as the events are all different.

The first thing you need to do is create a project to hold the code. In the spirit of expedition, I have created a “ Windows Communication Foundation  Service Application” to hold all of the Notification service code. But in the real world you would keep the Contract and Implementation classes in different assemblies.

TFS Event Handler in .NET 3.5 Part 2 - Handling Team Foundation Server Events  

Now we have the project, you can add the INotification class and the Notification service. I like to keep all of my services in a “v1.0” so that I can add other services in new versions without affecting the current version.

TFS Event Handler in .NET 3.5 Part 2 - Handling Team Foundation Server Events  

Once you have your INotification class looking like the code extract above we will add a default implementation and test the service. The default implementation should look like:

 1Imports Microsoft.TeamFoundation.Server
 2
 3Public Class Notification
 4    Implements INotification
 5
 6    Public Sub Notify(ByVal eventXml As String, ByVal tfsIdentityXml As String, ByVal SubscriptionInfo As SubscriptionInfo) Implements INotification.Notify
 7
 8    End Sub
 9
10End Class

 

This class just implements the INotification contract (interface) and has an empty Sub for the notify method that will be called when an event is fired in TFS.

 

The config file will contain a definition of the service as well as two endpoints for the same interface. The services and behaviours occur within the <system.serviceModel> tags.

1<system.serviceModel>
2  <services>
3   ...
4  </services>
5  <behaviors>
6   ...
7  </behaviors>
8</system.serviceModel>

       

This is the really important part of getting the service working, and goes between the <services> tags. The first part of the service configuration contains the service definition. Here we define the name of the service, which should be the same as full namespace and class name of out implementation, the behavior configuration name (we will look at this is a bit) and the endpoints.

1<service behaviorConfiguration="NotificationServiceBehavior" name="RDdotNet.TFSEventHandler.NotificationHost.Notification">
2    <endpoint address="WorkItemChangedEvent" binding="wsHttpBinding" contract="RDdotNet.TFSEventHandler.NotificationHost.INotification"/>
3    <endpoint address="CheckInEvent" binding="wsHttpBinding" contract="RDdotNet.TFSEventHandler.NotificationHost.INotification"/>
4    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
5</service>

 

In a service hosted within IIS there is no need to set a base address as the location of the .svc file sets this for us. In this case it is http://localhost:[port]/v1.0/Notification.svc  . The “mex” endpoint allows other application to discover the capabilities of the service.

The other two endpoint point at the same contract and are in fact implemented by the same method in the service implementation. These two endpoints have different addressed so that we can tell team server to send events to different URL’s but use the same code to process the events. You could add any of the TFS events in to this list of end points, but it is best to keep the names the same as the event being fired as we will detect it later. We are using the wsHttpBinding as the most advanced that TFS will support.

 

You only need to set two options on the service behavior, the httpGetEnabled needs to be set to true for the WSDL and the meta data to work. This allows the discoverability of you services. The includeExceptionDetailInFaults option allows the diagnosis of faults when we test the service.

1<serviceBehaviors>
2    <behavior name="NotificationServiceBehavior">
3        <serviceMetadata httpGetEnabled="true"/>
4        <serviceDebug includeExceptionDetailInFaults="true"/>
5    </behavior>
6</serviceBehaviors>

The service behaviors go between the <behaviors> tags and are named the same as the services behaviorConfiguration attribute.

We now have a working service and can test it by starting a new instance of the web application and going to the URL of the .svc file. You will see a page like:

TFS Event Handler in .NET 3.5 Part 2 - Handling Team Foundation Server Events  

This shows you how you can connect to the service, but as this will only be connected to by TFS it is not a requirement for just now. What is important is to see the URL’s for the two endpoints that we have created. If you click the WSDL URL (" http://localhost:65469/v1.0/Notification.svc?wsdl  ") you will see the generated meta data for the service.

I am not going to display the full WSDL here as it is huge, but here is the important section for what we need to look at:

 1<wsdl:service name="Notification">
 2  <wsdl:port name="WSHttpBinding_INotification" binding="tns:WSHttpBinding_INotification">
 3    <soap12:address location="http://localhost:65469/v1.0/Notification.svc/WorkItemChangedEvent"/>
 4    <wsa10:EndpointReference>
 5      <wsa10:Address>http://localhost:65469/v1.0/Notification.svc/WorkItemChanged</wsa10:Address>
 6      <Identity xmlns="http://schemas.xmlsoap.org/ws/2006/02/addressingidentity">
 7        <Upn>comuter1user1</Upn>
 8      </Identity>
 9    </wsa10:EndpointReference>
10  </wsdl:port>
11  <wsdl:port name="WSHttpBinding_INotification1" binding="tns:WSHttpBinding_INotification">
12    <soap12:address location="http://localhost:65469/v1.0/Notification.svc/CheckInEvent"/>
13    <wsa10:EndpointReference>
14      <wsa10:Address>http://localhost:65469/v1.0/Notification.svc/CheckIn</wsa10:Address>
15      <Identity xmlns="http://schemas.xmlsoap.org/ws/2006/02/addressingidentity">
16        <Upn>computer1user1</Upn>
17      </Identity>
18    </wsa10:EndpointReference>
19  </wsdl:port>
20</wsdl:service>

This section of the WSDL highlights the service definition and the two endpoints that exist. We are now able to get TFS to send events to this service. To do this you need to add the URL’s of the end points to the notification system within TFS. This is done by using a command line utility on the server, or by calling parts of the TFS API. For ease we will call the command line, but a future version should probably have a user interface to allow the administration of which TFS servers you want to handle events for.

To subscribe to the events you will need to use the BisSubscribe utility which you can find out how to use here  , or you can use the API and provide an interface to add and remove subscriptions.

If you call:

BisSubscribe.exe /userId TFSEventHandler /eventType WorkItemChangedEvent /deliveryType Soap /address http://localhost:65469/v1.0/Notification.svc/WorkItemChanged

Using the untility you will subscribe to events using SOAP, or if you call:

1SubscribeEvent(tfsServer, "TFSEventHandler", "http://localhost:65469/v1.0/Notification.svc/WorkItemChangedEvent", DeliveryType.Soap, Schedule.Imediate, EventType.WorkItemChangedEvent)

 

Using a API helper class similar to the one below:

 1    ''' <summary>
 2    ''' Helper methods for subscribing to and from events
 3    ''' </summary>
 4    ''' <remarks></remarks>
 5    Public Class SubscriptionHelper
 6
 7        Public Shared Function SubscribeEvent(ByRef tfs As TeamFoundationServer, ByVal userName As String, ByVal deliveryAddress As String, ByVal Type As Microsoft.TeamFoundation.Server.DeliveryType, ByVal Schedule As Microsoft.TeamFoundation.Server.DeliverySchedule, ByVal EventType As EventTypes, Optional ByVal Filter As String = "") As Integer
 8            Dim eventService As IEventService = CType(tfs.GetService(GetType(IEventService)), IEventService)
 9            Dim delivery As DeliveryPreference = New DeliveryPreference()
10            delivery.Type = Type
11            delivery.Schedule = Schedule
12            delivery.Address = deliveryAddress
13            Return eventService.SubscribeEvent(userName, EventType.ToString, Filter, delivery)
14        End Function
15
16        Public Shared Sub UnSubscribeEvent(ByRef tfs As TeamFoundationServer, ByVal subscriptionId As Integer)
17            Dim eventService As IEventService = CType(tfs.GetService(GetType(IEventService)), IEventService)
18            eventService.UnsubscribeEvent(subscriptionId)
19        End Sub
20
21    End Class

I may write an article in the future on this, but all the code is part of the current TFSEventHandler  application.

We now want to determine what sort of event has been raised in the Service implementation. To do this we need to parse the URL of the endpoint that has been raised and retrieve the event type.

1Dim UriString As String = OperationContext.Current.EndpointDispatcher.EndpointAddress.Uri.AbsoluteUri
2Dim SlashIndex As Integer = UriString.LastIndexOf("/")
3Dim EndieBit As String = UriString.Substring(SlashIndex, (UriString.Length - (UriString.Length - SlashIndex)))
4Dim EventType As EventTypes = CType([Enum].Parse(GetType(EventTypes), EndieBit), EventTypes)

 

As you can see it is just a case of parsing the URL to get the last bit after the final “/” and then converting it to an enumerator.

 1Public Enum EventTypes
 2    Unknown = 0
 3    AclChangedEvent
 4    Branchmovedevent
 5    BuildCompletionEvent
 6    BuildStatusChangeEvent
 7    CommonStructureChangedEvent
 8    DataChangedEvent
 9    IdentityChangedEvent
10    IdentityCreatedEvent
11    IdentityDeletedEvent
12    MembershipChangedEvent
13    WorkItemChangedEvent
14    CheckinEvent
15End Enum

  The enumerator lists all of the events that are possible in TFS, but be warned that not all of the events fire effectively. Once you have the event it can be converted into an object. I use code from Howard van Rooijen’s TFS Project Template  for the event objects and Larry Steinle’s CustomXmlSerializer  code to convert the XML to Howard’s objects resulting in the following code:

1Dim IdentityObject As TFSIdentity = EndpointBase.CreateInstance(Of TFSIdentity)(tfsIdentityXml)
2Dim EventObject As WorkItemChangedEvent = EndpointBase.CreateInstance(Of WorkItemChangedEvent)(eventXml)

 

All of the objects are now ready to pass over MSMQ to the TFS Event Processor, which will be the subject of the next article in this series…

Technorati Tags: Visual Studio Team System  , Visual Studio 2008  , Team Edition for Architects  , TFSEventHandler  , Microsoft .NET Framework  , Software Industrial Revolution  , WCF  , TFS Event Handler 

Software Development
Comments

Related blog posts

No related videos found.

Connect with Martin Hinshelwood

If you've made it this far, it's worth connecting with our principal consultant and coach, Martin Hinshelwood, for a 30-minute 'ask me anything' call.

Our Happy Clients​

We partner with businesses across diverse industries, including finance, insurance, healthcare, pharmaceuticals, technology, engineering, transportation, hospitality, entertainment, legal, government, and military sectors.​

ALS Life Sciences Logo
Capita Secure Information Solutions Ltd Logo
Ericson Logo
Xceptor - Process and Data Automation Logo
Alignment Healthcare Logo
Microsoft Logo
Teleplan Logo
Boeing Logo
Slaughter and May Logo
SuperControl Logo
MacDonald Humfrey (Automation) Ltd. Logo
Epic Games Logo

NIT A/S

Sage Logo
Slicedbread Logo
Kongsberg Maritime Logo
ProgramUtvikling Logo
Qualco Logo
New Hampshire Supreme Court Logo
Royal Air Force Logo
Washington Department of Enterprise Services Logo
Washington Department of Transport Logo
Ghana Police Service Logo
Department of Work and Pensions (UK) Logo
Teleplan Logo
Lockheed Martin Logo
Sage Logo
Jack Links Logo
Higher Education Statistics Agency Logo
Epic Games Logo