0

I am trying to update a field of a work-item using the managed TFS API.

However, certain states mark the field as read-only (for example, the Done state) and at that point I'm not able to modify the field, even when using the TFS API. I noticed that the IsEditable field returns False, but I cannot set it.

var effort = Convert.ToSingle(workItem.Fields["Effort"].Value);
workItem.Fields["Effort"].Value = effort / 2.0f;

As my code works for every state in which the field is editable I'm fairly confident it works. When checking the work-item using the Team Explorer UI I can see my changes are applied and tracked in the history of the work-item. This is also reflected in all of the reports that are generated.

How can I modify the field after it has been marked as read-only? Is it even possible? (I could switch the state back so the field is editable, but that's something I'd like to avoid as I have no idea what kind of impact this could have on my velocity and burndown charts.)

Jensen
  • 3,498
  • 2
  • 26
  • 43

2 Answers2

2

As it turns out, this cannot be done directly when using the managed API. When using the web-service API, there is a BypassRules property which can be set.

The web-service API requires an XML package, so the difficulty lies in creating it.

First, connect to the server and collection like you'd normally would.

var tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(
   new Uri(SERVER_URI), new UICredentialsProvider());
tpc.EnsureAuthenticated();

var store = tpc.GetService<WorkItemStore>();
var server = tpc.GetService<WorkItemServer>();

After I made the connection, I assembled a list of work-item ID's I wanted to modify. In my case, I needed to divide all work-item Efforts by 2.

The XML package syntax is fairly straight forward. One thing to note is that I explicitly needed to convert my effort value (a float) to a string, even though the internal type is double. (I might have done something wrong here, but the end result was what I wanted.)

private static StringBuilder GetXmlPackage(WorkItemStore store, IEnumerable<int> workItemIds)
{
    var sb = new StringBuilder();
    sb.Append("<Package>");

    foreach (var id in workItemIds)
    {
        var item = store.GetWorkItem(id);
        var effort = Convert.ToSingle(item.Fields["Effort"].Value) / 2.0f;

        sb.AppendFormat("<UpdateWorkItem ObjectType='WorkItem' BypassRules='1' WorkItemID='{0}' Revision='{1}'>", item.Id, item.Rev.ToString());
        sb.Append("<Columns>");
        sb.AppendFormat("<Column Column='Microsoft.VSTS.Scheduling.Effort' Type='Double'><Value>{0}</Value></Column>", XmlConvert.ToString(effort));
        sb.Append("</Columns>");
        sb.Append("<InsertText FieldName='System.History' FieldDisplayName='History'>Updated effort.</InsertText>");
        sb.Append("</UpdateWorkItem>");
    }

    sb.Append("</Package>");
    return sb;
}

The last thing to do is call the web-server and do a bulk update to modify all work-items.

private static void UpdateWorkItems(WorkItemStore store, WorkItemServer server, IEnumerable<int> workItemIds)
{
    var sb = GetXmlPackage(store, workItemIds);

    var mthe = new MetadataTableHaveEntry[0];
    string dbStamp;
    IMetadataRowSets rowSets;
    XmlElement outElement;

    var doc = new XmlDocument();
    doc.LoadXml(sb.ToString());

    server.BulkUpdate(
        WorkItemServer.NewRequestId(),
        doc.DocumentElement,
        out outElement,
        mthe,
        out dbStamp,
        out rowSets);
}
Jensen
  • 3,498
  • 2
  • 26
  • 43
  • is there any documentation on the possible formats? I know of this one, and also of the package format. But if there is documentation out there of the possible packages you can send using Update(), that would really be great, and help tons of people i'm sure as sometimes these are the only way to perform certain tasks (the managed API won't let you do) like delete a global list... Thx! – MaxOvrdrv Apr 03 '14 at 14:13
0

AFAIK you can do this with the API under a condition. The account that the process is running under must belong to the Project Collection Service Accounts group. Then when you create your WorkIteMStore with the BypassRules flag set, similar to the code below, you will be able to update the fields:

WorkItemStore wis = new WorkItemStore(tpc, WorkItemStoreFlags.BypassRules);