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:
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.
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.
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.
Figure: One lonely work item that did not get destroyed
To delete this errant work item you can:
You can get your work item ID from 0 to 40000 in around 40 minutes with this app which solves the immediate problem.
No related videos found.
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.
We partner with businesses across diverse industries, including finance, insurance, healthcare, pharmaceuticals, technology, engineering, transportation, hospitality, entertainment, legal, government, and military sectors.
Hubtel Ghana
Cognizant Microsoft Business Group (MBG)
Illumina
Genus Breeding Ltd
SuperControl
Sage
Alignment Healthcare
Qualco
Freadom
DFDS
Kongsberg Maritime
Ericson
Healthgrades
Capita Secure Information Solutions Ltd
Slaughter and May
Trayport
Epic Games
Boxit Document Solutions
Nottingham County Council
Washington Department of Transport
Department of Work and Pensions (UK)
Washington Department of Enterprise Services
Ghana Police Service
Royal Air Force
Healthgrades
Brandes Investment Partners L.P.
Xceptor - Process and Data Automation
Workday
Teleplan
Capita Secure Information Solutions Ltd