private IEnumerable GetRelatives(IEnumerable <IIdentifiable> source, RelativesExpression relativesExpression, Type itemType, DateTime asOf) { var results = Activator .CreateInstance(typeof(List <>) .MakeGenericType(itemType)) as IList; var ids = source.Select(item => item.Id); if (!ids.Any()) { return(results); } var workItemLinks = WorkItemStore.QueryLinks(GetQueryStringForRelativesOf(ids, relativesExpression.AncestorType, relativesExpression.DescendentType, relativesExpression.Direction, asOf)); var map = new Dictionary <int, List <int> >(); workItemLinks.Where(link => link.SourceId == 0).ToList().ForEach(link => map.Add(link.TargetId, new List <int>())); foreach (var ancestorId in map.Keys) { map[ancestorId].AddRange(GetTransitiveLinks(ancestorId, workItemLinks).Select(child => child.TargetId)); } ids = ids.Union(workItemLinks.Select(link => link.SourceId)).Union(workItemLinks.Select(link => link.TargetId)).Where(id => id != 0); var workItems = HydrateAndFilterItems(ids, relativesExpression.AncestorType, relativesExpression.DescendentType, asOf); foreach (var ancestorId in map.Keys) { var id = ancestorId; // Get the ancestor from the list of hydrated work items var ancestorWorkItem = workItems.Single(item => item.Id == ancestorId); // Filter the work items to just those IDs that are in the map for this ancestor. // NOTE: Please note that the strategy is to start with the hydrated work items and filter to // those in the map, and NOT to lookup every work item in the map. The latter strategy would // break because the map also contains the IDs of Task Groups that have been filtered out. // This way we start with work items that we already know exist. var descendentWorkItems = workItems.Where(wi => map[id].Contains(wi.Id)); var ancestor = WorkItemMapper.Create(relativesExpression.AncestorType, new[] { ancestorWorkItem }).Cast <object>().Single(); var descendents = WorkItemMapper.Create(relativesExpression.DescendentType, descendentWorkItems); var properTypedDescendents = GenerateListOfType(descendents, itemType); var result = Activator.CreateInstance(itemType, ancestor, properTypedDescendents); results.Add(result); } return(results); }