Azure DevOps REST API - Why does it take so long to retrieve parent/children work items?

Copper Contributor

I am creating a core MVC application to retrieve all Features and child PBIs from an Area Path and Iteration path. I am using the Get Work Item and Get Workitems Batch REST calls. When retrieving the data my solution is as follows:

  1. Get parent Feature data -- and a separate int[] list of the Parent Ids
  2. foreach id in the int[] list, call WorkitemBatch {ids, fields}
  3. iterate the child valies nd put in a child list

Running the code is successful, but retrieving 11 parent records with approximately 60-80 child records takes approximately 12 seconds to return. Is there a more performant process?

 

Code:

public async Task<List<ComplexWorkItem>> GetParentAndChildrenFields(RequestWithToken request, int[] wkItmIds)
{
string idList = string.Join(",", wkItmIds.Select(p => p.ToString()));

//Get original URI
string baseURI = request.RequestURI;

//expand the list of retrieved values --get everything you can
request.RequestURI = baseURI + $@"/_apis/wit/workitems?ids={idList}&$expand=all&api-version=6.0";
request.RequestBody = String.Empty;

try
{
string jsonString = await GetHttpResponseString(request);
InternalComplexWorkItem expandedWorkItem = JsonSerializer.Deserialize<InternalComplexWorkItem>(jsonString);
List<ComplexWorkItem> parentChildWorkItems = new List<ComplexWorkItem>();

int relatedIdsCount = 0;

//Iterate through the workitems
foreach (ExpandedWorkItemValue workItemParent in expandedWorkItem.value)
{
ComplexWorkItem complexWorkItem = new ComplexWorkItem();
WorkItemPart parentWorkItemParticle = new WorkItemPart();
//Add the parent to an itn array for use
//in the GetExpandedWorkItemIDs method
int[] parentId = new int[1];

//put back to baseURI
request.RequestURI = baseURI;

parentId[0] = workItemParent.id;
//Get the parent fields
InternalComplexWorkItem parentValue = await GetExpandedWorkItemIDs(request, parentId);
//Add to the parent segment of the ComplexWorkItem
#region Parent Segment
parentWorkItemParticle.WorkItemId = parentValue.value[0].id.ToString();
parentWorkItemParticle.WorkItemType = parentValue.value[0].fields.WorkItemType;
parentWorkItemParticle.WorkItemState = parentValue.value[0].fields.State;
parentWorkItemParticle.WorkItemTitle = parentValue.value[0].fields.Title;
parentWorkItemParticle.WorkItemAreaPath = parentValue.value[0].fields.AreaPath;
parentWorkItemParticle.WorkItemIterationPath = parentValue.value[0].fields.IterationPath;
parentWorkItemParticle.WorkItemCreatedDate = parentValue.value[0].fields.CreatedDate;
parentWorkItemParticle.WorkItemModifiedDate = parentValue.value[0].fields.ChangedDate;


complexWorkItem.ParentWorkItem = parentWorkItemParticle;
#endregion

InternalWorkItemBatch childItems = await GetAllChildWorkItemsBatch(request, workItemParent.id.ToString(), "Product Backlog Item");
//Now get the children
#region Child Segments
complexWorkItem.ChildWorkItems = new List<WorkItemPart>();
if (childItems.value != null)
{
for (int i = 0; i < childItems.value.Length; i++)
{
WorkItemPart childItemPart = new WorkItemPart();
childItemPart.WorkItemId = childItems.value[i].id.ToString();
childItemPart.WorkItemType = childItems.value[i].fields.WorkItemType;
childItemPart.WorkItemState = childItems.value[i].fields.State;
childItemPart.WorkItemTitle = childItems.value[i].fields.Title;
childItemPart.WorkItemAreaPath = childItems.value[i].fields.AreaPath;
childItemPart.WorkItemIterationPath = childItems.value[i].fields.IterationPath;
childItemPart.WorkItemCreatedDate = childItems.value[i].fields.CreatedDate;
childItemPart.WorkItemModifiedDate = childItems.value[i].fields.ChangedDate;
//Add the value
complexWorkItem.ChildWorkItems.Add(childItemPart);
}

}

#endregion

int test = 0;
parentChildWorkItems.Add(complexWorkItem);
}

return parentChildWorkItems;
}
catch (Exception exception)
{
throw exception;
}
finally
{
//return back to the original URI
request.RequestURI = baseURI;
}

}

 

private async Task<InternalWorkItemBatch> GetAllChildWorkItemsBatch(RequestWithToken request, string parentIDNumber, string childWorkItemType)//, string selectedIterationPathItems)
{
string baseURI = request.RequestURI;
InternalWorkItemBatch workItemChildBatch = new InternalWorkItemBatch();
InternalComplexWorkItem wkItm = new InternalComplexWorkItem();

//Make sure there is data
//Retrieve the query clause -- this is for PBIs now
WiqlStatement wiqlClass = new WiqlStatement
{
query = GenerateWiqlQuery(parentIDNumber, childWorkItemType)
};
string jsonBody = JsonSerializer.Serialize(wiqlClass);

request.RequestURI = baseURI + WIQL_STRING;
request.RequestBody = jsonBody;

//Need to get the list of child IDs
string jsonString = await GetHttpResponseString(request);
int[] workItemList = JsonSerializer.Deserialize<InternalWorkItemList>(jsonString).workItems.Select(x => x.id).ToArray();


if (workItemList.Length > 0)
{
//Get all the child IDS
InternalWorkItemBatchRequestBody batch = new InternalWorkItemBatchRequestBody
{
ids = workItemList,
fields = new string[]
{
"System.Id",
"System.State",
"System.Title",
"System.WorkItemType",
"System.AreaPath",
"System.IterationPath"
}
};


//update the request to get all workitems with the childidlist
string jsonBatchBody = JsonSerializer.Serialize<InternalWorkItemBatchRequestBody>(batch);

request.RequestURI = baseURI + $@"/_apis/wit/workitemsbatch?api-version=6.0";
request.RequestBody = jsonBatchBody;
request.HasBody = true;

string expandedWorkItemListjsonString = await GetHttpResponseString(request);

workItemChildBatch = JsonSerializer.Deserialize<InternalWorkItemBatch>(expandedWorkItemListjsonString);
}

return workItemChildBatch;
}

 

0 Replies