a·gen·tic a·gil·i·ty

TFS 2010 Work Item Seed: TFS Work Item system.id at a predefined number

Explains how to use the TFS 2010 API to set the starting Work Item ID by programmatically creating and deleting items, avoiding ID conflicts with other systems.

Published on
8 minute read
Image
https://nkdagility.com/resources/rRCNKBm5xct
Subscribe

This week my customer had a requirement to have all new work items created to have an ID greater than 40,000. They have another system that stores tickets and they don’t want any conflict.

There have been a number of ways to achieve this through a database change, but it is not supported by Microsoft. This means that if you make the change and mess it up you are on your own.

In order to provide a supported way of doing this you need to use the API, and that is an interesting problem. We are going to have to use the API to create a specific number of work items, but we don’t really want those work items hanging around.

We could:

  1. Create new Team Project
  2. Add 40,000 work items
  3. Delete the Team Project

But there should be a neater way and to that end, lets connect to a Team Project that we can use to create the work items in.

 1Dim projectPicker As New TeamProjectPicker(TeamProjectPickerMode.SingleProject, False)
 2projectPicker.ShowDialog()
 3
 4If Not projectPicker.SelectedTeamProjectCollection Is Nothing Then
 5
 6    projectPicker.SelectedTeamProjectCollection.EnsureAuthenticated()
 7
 8    _Store = projectPicker.SelectedTeamProjectCollection.GetService(Of WorkItemStore)()
 9    _ProjectInfo = projectPicker.SelectedProjects(0)
10
11End If

We can use the TeamProjectPicker class that the product team kindly provided to show the built in dialog to select a Team Project Collection and a a Team Project.

TFS 2010 Work Item Seed: TFS Work Item system.id at a predefined number

Figure: Getting the user to select a Team Project could not be easyer

This dialog has some different modes depending on what you are trying to achieve. You can set it to select either a Team Project Collection, one Team Project or many Team Projects.

1Public Enum TeamProjectPickerMode
2    ' Fields
3    MultiProject = 2
4    NoProject = 0
5    SingleProject = 1
6End Enum

Now that we have access to a WorkItemStore and know what Team Project to use to create the work items, we can start creating work items.

1Dim wi As WorkItem = project.WorkItemTypes(0).NewWorkItem
2wi.Title = String.Format("Seedgen {0} : Created by Hinshlabs TFS Work Item Seed Generator", i)
3wi.Description = String.Format("As a TFS Admin I want to have all new work items start at a number {0} larger than the current number", _WorkItemCount)
4wi.Save()

This may fail as all I am doing is picking the first work item type that is available. A better solution would be to select the first work item type in the “Requirement” category, or to give the user a list to select whatever he wants. This is just a throw away, one time bit if code, so as long as it works…

Just in case any of these work items are left at the end I have added a default Title and Description.

Once we have created this work item, we need to immediately delete it. Now, this is something that can’t be done using the UI, but can be done in code.

1Dim WioError As IEnumerable(Of WorkItemOperationError)
2Dim ids As Integer() = {Convert.ToInt32(wi.Id)}
3WioError = _Store.DestroyWorkItems(ids)

A tiny bit of code that solves a big problem. In versions before TFS 2010 this could only be done using the Power Tool, but the product team have seen the light and added it into the core product. Remember that Deleting a Work Item CAN’T be undone and once it is gone, it is gone.

I have wrapped all of this into a ViewModel that can be used in an MVVM WPF application.

  1Imports Microsoft.TeamFoundation.Client
  2Imports Microsoft.TeamFoundation.WorkItemTracking.Client
  3Imports Microsoft.TeamFoundation.Server
  4Imports GalaSoft.MvvmLight.Command
  5Imports GalaSoft.MvvmLight.Threading
  6Imports System.Windows.Threading
  7Imports System.Threading
  8
  9''' <summary>
 10''' Seeding View Model using the MVVM Light framework
 11''' </summary>
 12''' <remarks></remarks>
 13Public Class WorkItemSeedViewModel
 14    Inherits GalaSoft.MvvmLight.ViewModelBase
 15
 16    Private _SelectServerCommand As RelayCommand
 17    Private _SeedWorkItemIdCommand As RelayCommand
 18    Private _Store As WorkItemStore
 19    Private _ProjectInfo As ProjectInfo
 20    Private _WorkItemCount As Integer = 0
 21    Private _IsProcessing As Boolean = False
 22    Private _LastWorkItemId As Integer = 0
 23    Private _CurrentlyProcessingItem As Integer = 0
 24
 25    Public ReadOnly Property SelectTeamProjectCommand As RelayCommand
 26        Get
 27            Return _SelectServerCommand
 28        End Get
 29    End Property
 30
 31    Public ReadOnly Property SeedWorkItemIdCommand As RelayCommand
 32        Get
 33            Return _SeedWorkItemIdCommand
 34        End Get
 35    End Property
 36
 37    Public Property IsProcessing As Boolean
 38        Get
 39            Return _IsProcessing
 40        End Get
 41        Set(ByVal value As Boolean)
 42            If Not value.Equals(_IsProcessing) Then
 43                Dim oldValue As Integer = _IsProcessing
 44                _IsProcessing = value
 45                MyBase.VerifyPropertyName("IsProcessing")
 46                MyBase.RaisePropertyChanged(Of Boolean)("IsProcessing", oldValue, value, True)
 47            End If
 48        End Set
 49    End Property
 50
 51    Public Property WorkItemCount As Integer
 52        Get
 53            Return _WorkItemCount
 54        End Get
 55        Set(ByVal value As Integer)
 56            If Not value.Equals(_WorkItemCount) Then
 57                Dim oldValue As Integer = _WorkItemCount
 58                _WorkItemCount = value
 59                MyBase.VerifyPropertyName("WorkItemCount")
 60                MyBase.RaisePropertyChanged(Of Integer)("WorkItemCount", oldValue, value, True)
 61            End If
 62        End Set
 63    End Property
 64
 65    Public Property LastWorkItemId As Integer
 66        Get
 67            Return _LastWorkItemId
 68        End Get
 69        Set(ByVal value As Integer)
 70            If Not value.Equals(_LastWorkItemId) Then
 71                Dim oldValue As Integer = _LastWorkItemId
 72                _LastWorkItemId = value
 73                MyBase.VerifyPropertyName("LastWorkItemId")
 74                MyBase.RaisePropertyChanged(Of Integer)("LastWorkItemId", oldValue, value, True)
 75            End If
 76        End Set
 77    End Property
 78
 79    Public Property CurrentlyProcessingItem As Integer
 80        Get
 81            Return _CurrentlyProcessingItem
 82        End Get
 83        Set(ByVal value As Integer)
 84            If Not value.Equals(_CurrentlyProcessingItem) Then
 85                Dim oldValue As Integer = _CurrentlyProcessingItem
 86                _CurrentlyProcessingItem = value
 87                MyBase.VerifyPropertyName("CurrentlyProcessingItem")
 88                MyBase.RaisePropertyChanged(Of Integer)("CurrentlyProcessingItem", oldValue, value, True)
 89            End If
 90        End Set
 91    End Property
 92
 93    Public Sub New()
 94        ' Create and wire up the Commands that I will be using
 95        _SelectServerCommand = New GalaSoft.MvvmLight.Command.RelayCommand(AddressOf OnSelectServerCommandExecute)
 96        _SeedWorkItemIdCommand = New GalaSoft.MvvmLight.Command.RelayCommand(AddressOf OnSeedWorkItemIdCommandExecute, Function() Not _ProjectInfo Is Nothing And Not IsProcessing)
 97    End Sub
 98
 99    ''' <summary>
100    ''' Calls the TFS 2010 API Dialog for selecting a Single Team Project and saves the WorkItemStore and ProjectInfo instances
101    ''' </summary>
102    ''' <remarks>Called by the Command object</remarks>
103    Private Sub OnSelectServerCommandExecute()
104        Dim projectPicker As New TeamProjectPicker(TeamProjectPickerMode.SingleProject, False)
105        projectPicker.ShowDialog()
106
107        If Not projectPicker.SelectedTeamProjectCollection Is Nothing Then
108
109            projectPicker.SelectedTeamProjectCollection.EnsureAuthenticated()
110
111            _Store = projectPicker.SelectedTeamProjectCollection.GetService(Of WorkItemStore)()
112            _ProjectInfo = projectPicker.SelectedProjects(0)
113
114        End If
115
116        _SeedWorkItemIdCommand.RaiseCanExecuteChanged()
117
118    End Sub
119
120    ''' <summary>
121    ''' Starts the Work Item generating method on a thread
122    ''' </summary>
123    ''' <remarks></remarks>
124    Private Sub OnSeedWorkItemIdCommandExecute()
125        Dim thread As New System.Threading.Thread(New ParameterizedThreadStart(Sub() ProcessWorkItems(_WorkItemCount)))
126        thread.Start()
127    End Sub
128
129    ''' <summary>
130    ''' Generates and then deletes each work item for the count specified by the value of Count
131    ''' </summary>
132    ''' <param name="count"></param>
133    ''' <remarks></remarks>
134    Private Sub ProcessWorkItems(ByVal count As Integer)
135        ' These calls martial the action code back onto the UI thread
136        DispatcherHelper.CheckBeginInvokeOnUI(New Action(Sub() IsProcessing = True))
137
138        Dim project As Project = _Store.Projects(_ProjectInfo.Name)
139
140        For i As Integer = 0 To count - 1
141            DispatcherHelper.CheckBeginInvokeOnUI(New Action(Sub() CurrentlyProcessingItem = i))
142
143            Dim wi As WorkItem = project.WorkItemTypes(0).NewWorkItem
144
145            wi.Title = String.Format("Seedgen {0} : Created by Hinshlabs TFS Work Item Seed Generator", i)
146            wi.Description = String.Format("As a TFS Admin I want to have all new work items start at a number {0} larger than the current number", _WorkItemCount)
147            wi.Save()
148
149            DispatcherHelper.CheckBeginInvokeOnUI(New Action(Sub() LastWorkItemId = wi.Id))
150
151
152            Dim WioError As IEnumerable(Of WorkItemOperationError)
153
154            Dim ids As Integer() = {Convert.ToInt32(wi.Id)}
155
156            WioError = _Store.DestroyWorkItems(ids)
157            If WioError.Count > 0 Then
158                ' Currently eats and failures to delete. Not good, but easy to fix
159                'MessageBox.Show(String.Format("Failed to delete Work Item {0}", wi.Id))
160                Exit For
161            Else
162                DispatcherHelper.CheckBeginInvokeOnUI(New Action(Sub() WorkItemCount = WorkItemCount - 1))
163
164            End If
165        Next
166        DispatcherHelper.CheckBeginInvokeOnUI(New Action(Sub() IsProcessing = False))
167    End Sub
168
169
170End Class

I have tried to comment at least some of this, but again this is proof-of-concept code with no warranty and could do with lots of improving.

This is a supported method of incrementing the Work Item ID to any number you like. It will take a while to get to 40,000 but I have added a “Time taken” calculation and it looks like you can increment the top ID by around 600 every minute.

TFS 2010 Work Item Seed: TFS Work Item system.id at a predefined number

Figure: As you can see, I am an artist

I have been running this with a local TFS running on my Windows 7 laptop with SQL Express so actual times may not be as advertised.

How stable is this? Well, to be honest, not very. I threw this together quickly, but with all my debugging, killing and general mashing it only left 1 erroneous work item that will need to be deleted manually.

TFS 2010 Work Item Seed: TFS Work Item system.id at a predefined number

Figure: One lonely work item that did not get destroyed

To delete this errant work item you can:

Conclusion

You can get your work item ID from 0 to 40000 in around 40 minutes with this app which solves the immediate problem.

TFS 2010 Work Item Seed: TFS Work Item system.id at a predefined number

Technorati Tags: TFS , TFS 2010 , API

Software Development Windows
Subscribe

Related blog

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.​

Hubtel Ghana Logo

Hubtel Ghana

Cognizant Microsoft Business Group (MBG) Logo

Cognizant Microsoft Business Group (MBG)

Illumina Logo

Illumina

Genus Breeding Ltd Logo

Genus Breeding Ltd

SuperControl Logo

SuperControl

Sage Logo

Sage

Alignment Healthcare Logo

Alignment Healthcare

Qualco Logo

Qualco

Freadom Logo

Freadom

DFDS Logo

DFDS

Kongsberg Maritime Logo

Kongsberg Maritime

Ericson Logo

Ericson

Healthgrades Logo

Healthgrades

Capita Secure Information Solutions Ltd Logo

Capita Secure Information Solutions Ltd

Slaughter and May Logo

Slaughter and May

Trayport Logo

Trayport

Epic Games Logo

Epic Games

Boxit Document Solutions Logo

Boxit Document Solutions

Nottingham County Council Logo

Nottingham County Council

Washington Department of Transport Logo

Washington Department of Transport

Department of Work and Pensions (UK) Logo

Department of Work and Pensions (UK)

Washington Department of Enterprise Services Logo

Washington Department of Enterprise Services

Ghana Police Service Logo

Ghana Police Service

Royal Air Force Logo

Royal Air Force

Healthgrades Logo

Healthgrades

Brandes Investment Partners L.P. Logo

Brandes Investment Partners L.P.

Xceptor - Process and Data Automation Logo

Xceptor - Process and Data Automation

Workday Logo

Workday

Teleplan Logo

Teleplan

Capita Secure Information Solutions Ltd Logo

Capita Secure Information Solutions Ltd