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

Timothy Fischer 121 Reputation points
2023-02-20T18:27:43.51+00:00

This is specific to Azure DevOps REST API library, but there are no tags for it

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:

Get parent Feature data -- and a separate int[] list of the Parent Ids

foreach id in the int[] list, call WorkitemBatch {ids, fields}

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;

}

ASP.NET Core
ASP.NET Core
A set of technologies in the .NET Framework for building web applications and XML web services.
4,167 questions
ASP.NET
ASP.NET
A set of technologies in the .NET Framework for building web applications and XML web services.
3,256 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,251 questions
0 comments No comments
{count} vote