I have a recursive setup of Project
s. The entity model is defined like this:
public class Project
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string ProjectType { get; set; } // "project" or "task"
public string Title { get; set; }
// The next three values are always 0 for projects of type "project".
// Only projects of type "task" has these values set:
public int EstimatedScheduleWeeks { get; set; }
public int EstimatedScheduleDays { get; set; }
public int EstimatedScheduleHours { get; set; }
public List<Project> ChildProjects { get; set; }
}
A Project
of type "project" can have unlimited levels of child projects, each of which can also have tasks. A Project
of type "task" can not have child items.
In the view model I have the property CalculatedHours
, which is supposed to summarize all the EstimatedSchedule
-values from all the descendant Project
s:
public int CalculatedHours
{
get
{
if (ProjectType == "task")
{
return
(EstimatedScheduleWeeks * 40) +
(EstimatedScheduleDays * 8) +
EstimatedScheduleHours;
}
if (ProjectType == "project" && ChildProjects != null)
{
IEnumerable<ProjectViewModel> childProjects =
ItemDescendantsFlat(ChildProjects, Id);
int weeks = childProjects.Select(w => w.EstimatedScheduleWeeks).Sum();
int days = childProjects.Select(w => w.EstimatedScheduleDays).Sum();
int hours = childProjects.Select(w => w.EstimatedScheduleHours).Sum();
// A week has 40 hours, a day has 8:
return (weeks * 40) + (days * 8) + hours;
}
return 0;
}
}
private static IEnumerable<ProjectViewModel>
ItemDescendantsFlat(IEnumerable<ProjectViewModel> src, int parentId)
{
// https://stackoverflow.com/questions/48453796/linq-recursive-sum
// NetMage's answer:
var childItems = src.ToLookup(i => i.ParentId);
var stackOfChildren = new Stack<IEnumerable<ProjectViewModel>>();
stackOfChildren.Push(childItems[parentId]);
do
foreach (var c in stackOfChildren.Pop())
{
yield return c;
stackOfChildren.Push(childItems[c.Id]);
}
while (stackOfChildren.Count > 0);
}
When I inspect childProjects
in the CalculatedHours
-property, all the Project
s and descendants are there. But they are not in a flat list. They are still structured like a recursive tree. Maybe that is why the returned value is only the sum of the EstimatedSchedule
-variables from the first level of Project
s, with type "task".
I'm pretty sure the ItemDescendantsFlat()
-method works. Maybe I'm not using it correctly? How can I summarize the EstimatedSchedule
-values from all the descendants?
I'm referencing this question in the code (NetMage's answer).