{"id":10832,"date":"2016-01-13T13:44:00","date_gmt":"2016-01-13T13:44:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/visualstudioalm\/2016\/01\/13\/identities-and-work-item-tracking-in-tfs-2015\/"},"modified":"2022-08-02T23:55:26","modified_gmt":"2022-08-03T07:55:26","slug":"identities-and-work-item-tracking-in-tfs-2015","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/devops\/identities-and-work-item-tracking-in-tfs-2015\/","title":{"rendered":"Identities and Work Item Tracking in TFS 2015"},"content":{"rendered":"<p>In Team Foundation Server 2015 we introduced identity fields which made significant changes to how the Work Item Tracking (WIT) system handles identity values. These changes help modernize the underlying system and provide support for upcoming features . However, the changes result in API and data format differences that TFS users (especially those writing third-party tools with ClientOM or REST) need to be aware of.<\/p>\n<p>\u00a0<\/p>\n<h2>History<\/h2>\n<p>Historically, the WIT system didn\u2019t handle identities well. Before TFS 2015, all fields that dealt with identities treated identities as just plain strings that represented a user\u2019s display name. This resulted in some odd situations especially around ambiguous identities. If you attempted to assign a work item to \u201cJamal Hartnett\u201d, and two users named \u201cJamal Hartnett\u201d existed in the system, the system would not ask you which one you meant. Instead, it would create a third \u201cvirtual\u201d Jamal Hartnett and assign the work item to him. This inability to disambiguate similarly named accounts presents a problem for the WIT system with repercussions in query, charting, assignment, and capacity. \u00a0<\/p>\n<p>\u00a0<\/p>\n<h2>Identity Fields<\/h2>\n<p>The TFS 2015 WIT system now distinguishes identity fields separate from simply plain text fields, however, they come with some new behaviors and restrictions.\u00a0<\/p>\n<ul>\n<li>Identity fields are intended to uniquely identify identities.<\/li>\n<li>They still support non-identity values (like \u201cNot Yet Assigned\u201d) but these are not recommended.<\/li>\n<li>Identity\u00a0fields are determined by examining what rules are set on a field (except for the core identity fields like\u00a0AssignedTo,\u00a0ChangedBy,CreatedBy\u00a0which are always considered identity fields regardless of rules).\u00a0<\/li>\n<\/ul>\n<div>\n  \u00a0 <\/p>\n<p>\n    In order to support existing process templates, we use an inference approach to determine identity fields. We consider a field an identity field if it contains any of the following:\n  <\/p>\n<ol>\n<li>\n      A valid user rule\n    <\/li>\n<li>\n      A group scoping allowed or prohibited value rule\n    <\/li>\n<li>\n      An individual user allowed or prohibited value rule\n    <\/li>\n<\/ol>\n<p>\n    \u00a0\n  <\/p>\n<p>\n    For example, the following field definition defines an identity field because of the ValidUser rule:\n  <\/p>\n<p>  <code> &lt;FIELD name=\"My Identity\" refname=\"My.Identity\" type=\"String\" syncnamechanges=\"true\" &gt; &lt;br \/>&nbsp; &lt;ALLOWEXISTINGVALUE \/&gt; &lt;br \/>&nbsp; &lt;VALIDUSER \/&gt; &lt;br \/>&lt;\/FIELD&gt; <\/code> <\/p>\n<p>\n    This one makes use of an Allowed Values rule to specify an identity:\n  <\/p>\n<p>  <code> &lt;FIELD name=\"Another Identity\" refname=\"Another.Identity\" type=\"String\" syncnamechanges=\"true\" &gt;&lt;br \/>&nbsp; &lt;ALLOWEDVALUES&gt; &lt;br \/>&nbsp; &nbsp; &lt;LISTITEM value=\"[global]Project Collection Valid Users\" \/&gt; &lt;br \/>&nbsp; &lt;\/ALLOWEDVALUES&gt;&lt;br \/> &lt;\/FIELD&gt; <\/code> <\/p>\n<p>\n    \u00a0\n  <\/p>\n<p>\n    Both of the examples show the\u00a0syncnamechanges\u00a0property set to true. It is important to set this to true for identity fields to ensure they work correctly when a user changes their name. If you have an identity field with this property not set you can change it (as of TFS 2015 Update 1)\u00a0<a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/dd236909.aspx\">using\u00a0witadmin<\/a>.\n  <\/p>\n<p>\n    \u00a0\n  <\/p>\n<p>\n    <strong>Note:<\/strong>\u00a0Identity fields are scoped to the entire team project collection. If one work item\u00a0type\u00a0indicates that a field is an identity, this field becomes an identity field for all types in the collection.\n  <\/p>\n<p>\n    Once the system marks a field as an identity field several changes occur: it gets a new UI control and the REST API and\u00a0ClientOM\u00a0change what values they accept and return.\n  <\/p>\n<p>\n    \u00a0\n  <\/p>\n<h2>\n    Identity Control\n  <\/h2>\n<p>\n    All identity fields get a new picker which treats identities as first class values. In the web portal, this picker contains an MRU (most recently used) + the ability to search for all other identities.\n  <\/p>\n<\/div>\n<div>\n  \u00a0\n<\/div>\n<div>\n  \u00a0<a href=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/01\/8741.idPicker.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/01\/8741.idPicker.png\" alt=\"\" border=\"0\" \/><\/a>\n<\/div>\n<div>\n  \u00a0\n<\/div>\n<div>\n  \u00a0One big change with this picker is that it\u2019s no longer scoped based on rules. The picker will search all valid identities in your collection. If you pick a value that is invalid based on your rules, it will validate and report a field error before you save. Search is not scoped for a few reasons:\n<\/div>\n<div>\n<ol>\n<li>\n      Consistent with how majority of users are using identities\n    <\/li>\n<li>\n      Improved Performance\n    <\/li>\n<li>\n      Support for Azure Active Directory (AAD)\n    <\/li>\n<\/ol>\n<p>\n    \u00a0\n  <\/p>\n<h2>\n    REST APIs\n  <\/h2>\n<p>\n    The data returned and accepted by\u00a0<a href=\"https:\/\/www.visualstudio.com\/integrate\/api\/wit\/overview\">\u00a0WIT REST API\u2019s<\/a>\u00a0for identity fields has changed. Reading a value for an identity now returns a combo-string that contains the user\u2019s display name followed by their unique name in brackets. For example, the following GET response\u00a0CreatedBy\u00a0field contains the value \u201cJamal Hartnett <fabrikamfiber4@hotmail.com>&#8220;. The system specifies the name disambiguated by the user account, instead of returning just the name, \u201cJamal Harnett\u201d.\n  <\/p>\n<p>\n    \u00a0\n  <\/p>\n<p>\n    <a href=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/01\/6242.getWorkitems.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/01\/6242.getWorkitems.png\" alt=\"\" border=\"0\" \/><\/a>\n  <\/p>\n<\/div>\n<div>\n<p>\n    When posting an update to a work item or querying work items, the REST API\u2019s now accept that same combo-string in order to unambiguously refer to an identity.\n  <\/p>\n<p>\n    \u00a0\n  <\/p>\n<p>\n    <strong>Query<\/strong>\n  <\/p>\n<p>\n    For query you can post WIQL like the following to find all work items assigned to \u201cJamal Hartnett <fabrikamfiber4@hotmail.com>&#8220;:\n  <\/p>\n<\/div>\n<div>\n  <a href=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/01\/4405.runWiqlCombo.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/01\/4405.runWiqlCombo.png\" alt=\"\" border=\"0\" \/><\/a>\n<\/div>\n<div>\n  \u00a0\n<\/div>\n<div>\n  \u00a0\n<\/div>\n<div>\n<p>\n    By passing the combo-string, only the identities assigned to this particular identity are returned. However, if you were to omit the email\/alias portion of the string and just pass the display name, it would still work as expected except if the name was ambiguous. If there were two Jamal Hartnett\u2019s, then the following query would return items from both. This makes sense because we are not uniquely\u00a0identifying\u00a0which one we want:\n  <\/p>\n<p>\n    \u00a0<a href=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/01\/5076.runWiqlDisplayName.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/01\/5076.runWiqlDisplayName.png\" alt=\"\" border=\"0\" \/><\/a>\n  <\/p>\n<\/div>\n<div>\n<p>\n    \u00a0\n  <\/p>\n<p>\n    <strong>Updates<\/strong>\n  <\/p>\n<p>\n    Updating an identity field using the REST API is similar. It is best to pass the combo-string to unambiguously refer to an identity. However, if you pass just the display name it would still work correctly unless that name is ambiguous. If you post the following update to set\u00a0System.AssignedTo\u00a0to Jamal Hartnett and he was an ambiguous identity, you would get an error message explaining that.\n  <\/p>\n<p>\n    \u00a0\n  <\/p>\n<p>\n    \u00a0<a href=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/01\/1207.updateWOrkItem.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/01\/1207.updateWOrkItem.png\" alt=\"\" border=\"0\" \/><\/a>\n  <\/p>\n<\/div>\n<div>\n  <span>Returns error (status code 400):<\/span>\n<\/div>\n<p style=\"padding-left: 30px\">\n  <code>\"message\": \"The value 'Jamal Hartnett' for field 'Assigned To' is ambiguous with 'Jamal Hartnett ;Jamal Hartnett '. Provide a unique name for this field.\" &lt;br \/>\"typeName\": \"Microsoft.TeamFoundation.WorkItemTracking.Server.WorkItemFieldInvalidException, Microsoft.TeamFoundation.WorkItemTracking.Server, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a\" &lt;br \/>\"typeKey\": \"WorkItemFieldInvalidException\"<\/code>\n<\/p>\n<p style=\"padding-left: 30px\">\n  \u00a0\n<\/p>\n<p>If you pass the combo-string in this case it would resolve the issue and save correctly.<\/p>\n<p>\u00a0<\/p>\n<h2>ClientOM<\/h2>\n<p>Identity fields in the TFS 2015 version of the WIT\u00a0ClientOM\u00a0have changed to ensure that identity values are always read\/written uniquely. Because of how the\u00a0ClientOM\u00a0is architected, embedding the flexibility of multiple data formats without breaking existing applications presented a challenge.\u00a0For this reason, the identity strings in the\u00a0ClientOM\u00a0behave differently than in the RESTAPI.<\/p>\n<p>When you read from an identity field and that user\u2019s display name is unique you will get the display name returned:<\/p>\n<p><code>&amp;nbsp; var workItemStore = teamProjectCollection.GetService();&lt;br \/&gt;&amp;nbsp; var workItem = workItemStore.GetWorkItem(1);&lt;br \/&gt;&amp;nbsp; Console.WriteLine(workItem.Fields[\"System.AssignedTo\"].Value); \/\/ Jamal Hartnett<\/code><\/p>\n<p>\u00a0<\/p>\n<p>However, if the system has more than one Jamal Hartnett the field returns the combo-string:<\/p>\n<p><code>&amp;nbsp; var workItemStore = teamProjectCollection.GetService();&lt;br \/&gt;&amp;nbsp; var workItem = workItemStore.GetWorkItem(1);&lt;br \/&gt;&amp;nbsp; Console.WriteLine(workItem.Fields[\"System.AssignedTo\"].Value); \/\/ Jamal Hartnett &lt;fabrikamfiber4@hotmail.com&gt;<\/code><\/p>\n<p>\u00a0<\/p>\n<p>The\u00a0ClientOM\u00a0tries to only show the combo-string when needed. However, it also requires the correct value when assigning an identity field. So, if you\u2019re assigning a value to an identity field, you need to make sure you pass the proper value. Fortunately, there is a helper function to make this easy called GetValidDisplayName. This method has two overloads, one takes a\u00a0TeamFoundationIdentity\u00a0object and the other takes a\u00a0DisplayName\u00a0and\u00a0UniqueName.<\/p>\n<p>Before assigning to an identity field you should call this method to ensure you are getting the right value to assign to.<\/p>\n<p>\u00a0<\/p>\n<p><strong>Example 1<\/strong><\/p>\n<p><code>&amp;nbsp; var identityService = teamProjectCollection.GetService(); &lt;br \/&gt;&amp;nbsp; var user = identityService.ReadIdentity(IdentitySearchFactor.AccountName, \"fabrikamfiber4@hotmail.com\", MembershipQuery.None, ReadIdentityOptions.None); &lt;br \/&gt;&amp;nbsp; workItem.Fields[\"System.AssignedTo\"].Value = workItemStore.GetValidDisplayName(user);<\/code><\/p>\n<p>\u00a0<\/p>\n<p><strong>Example 2<\/strong><\/p>\n<p><code>&amp;nbsp; workItem.Fields[\"System.AssignedTo\"].Value = workItemStore.GetValidDisplayName(\"Jamal Hartnett\", \"fabrikamfiber4@hotmail.com\");<\/code><\/p>\n<div>\n  &#8211; Matthew Manela\u00a0<a href=\"https:\/\/twitter.com\/mmanela\">@mmanela<\/a>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>In Team Foundation Server 2015 we introduced identity fields which made significant changes to how the Work Item Tracking (WIT) system handles identity values. These changes help modernize the underlying system and provide support for upcoming features . However, the changes result in API and data format differences that TFS users (especially those writing third-party [&hellip;]<\/p>\n","protected":false},"author":196,"featured_media":45953,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-10832","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops"],"acf":[],"blog_post_summary":"<p>In Team Foundation Server 2015 we introduced identity fields which made significant changes to how the Work Item Tracking (WIT) system handles identity values. These changes help modernize the underlying system and provide support for upcoming features . However, the changes result in API and data format differences that TFS users (especially those writing third-party [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/posts\/10832","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/users\/196"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/comments?post=10832"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/posts\/10832\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/media\/45953"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/media?parent=10832"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/categories?post=10832"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/tags?post=10832"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}