Custom TFS Check In Policy Responsiveness

I’ve used several third party Check In Policies for Team Foundation Server with both TFS2005 and 2008 and I’ve dabbled in writing my own too. One thing I noticed with most of them, is that they don’t appear to respond to actions in the VS Pending Changes window as readily as the standard Microsoft policies.

I recently followed Jim’s example Option Strict Check In Policy to write my own policy to prevent checking in code with the DataSet Designer ConnectionState Bug. The policy would always correctly evaluate when clicking the Check In button but the Policy Warnings tab didn’t always update automatically when Source Files list changed.

I decided it was time to dig deeper and find out why the standard policies work nicely when compared to the custom ones. After a few minutes with Reflector I discovered there were a few more things to do beyond the instructions in the MSDN article to create a responsive custom policy.

The PolicyBase class, from which your custom policy should inherit, gets passed an instance of IPendingCheckin to its Initialize method which it persists and is made available to your subclass via a protected PendingCheckin property. The IPendingCheckin instance exposes several other objects with events that you can handle to be notified when changes relevant to your policy occur.

The methodology that worked for me was to override Initialize and register the event handler and override Dispose also to remove the handler at the end. All the handler does, after checking the base class isn’t disposed, is to call the custom policy’s Evaluate function and raise the base’s PolicyStateChanged event.

Sample code for a policy dependent on the Source Files list, like the Option Strict policy and the ConnectionState Bug policy, follows:

Public Overrides Sub Initialize(ByVal pendingCheckin As IPendingCheckin)
MyBase.Initialize(pendingCheckin)
AddHandler MyBase.PendingCheckin.PendingChanges.CheckedPendingChangesChanged, _
AddressOf CheckedPendingChangesChanged
End Sub

Private Sub CheckedPendingChangesChanged(ByVal sender As Object, ByVal e As EventArgs)
If Not MyBase.Disposed Then
Dim failures = Evaluate()
MyBase.OnPolicyStateChanged(failures)
End If
End Sub

Public Overrides Sub Dispose()
RemoveHandler MyBase.PendingCheckin.PendingChanges.CheckedPendingChangesChanged, _
AddressOf CheckedPendingChangesChanged
MyBase.Dispose()
End Sub