I have a bunch of code that deals with modifying a large nested set of IEnumerable<DTO's>
Basically, this is the structure of the DTO's
public class ProductLineDto
{
...
public IEnumerable<ProjectTypeDto> ProjectType { get; set; }
}
public class ProjectTypeDto
{
...
public IEnumerable<ProjectDto> Projects { get; set; }
}
public class ProjectDto
{
...
public IEnumerable<SubProjectsDto> SubProjects { get; set; }
}
public class SubProjectDto
{
...
public IEnumerable<SubProjectDto> Children { get; set; }
public IEnumerable<ActivityDto> Activities { get; set; }
}
Here is what the code that uses them looks like.
I was wondering if there was a way to clean up the code. I don't like to have to make a copy of every nested
IEnumerable
to modify theganttData
variable. I was thinking of maybe making an extension method take care of that for me, that way I wouldn't have to throw those two lines of code in for everyIEnuemerable
Are there any performance enhancements I can make? For instance, would it be faster to use
Select()
orWhere()
instead ofRemoveAll()
?public IEnumerable<ProductLineDto> FilterGanttChartData(IEnumerable<ProductLineDto> ganttData, GanttFilterDto ganttFilterDataModel) { ... ganttData = GetDataByEndDate(ganttData.ToList(), ganttFilterDataModel); return ganttData; } private IEnumerable<ProductLineDto> GetDataByEndDate(List<ProductLineDto> ganttData, GanttFilterDto ganttFilterDataModel) { if (ganttFilterDataModel.EndDateFrom == null && ganttFilterDataModel.EndDateTo == null) return ganttData; var gdata = new List<ProductLineDto>(); gdata.AddRange(ganttData); for (var i = 0; i < gdata.Count; i++) { var pType = new List<ProjectTypeDto>(); pType.AddRange(gdata[i].ProjectType); for (var j = 0; j < pType.Count; j++) { var projects = new List<ProjectDto>(); projects.AddRange(pType[j].Projects); for (var a = 0; a < projects.Count; a++) { var subProjects = new List<SubProjectDto>(); subProjects.AddRange(projects[a].Children); subProjects = GetActivitiesByEndDateRange(subProjects, ganttFilterDataModel.EndDateFrom, ganttFilterDataModel.EndDateTo).ToList(); projects[a].Children = subProjects; } pType[j].Projects = projects; } gdata[i].ProjectType = pType; } ganttData = gdata; return ganttData; }
Then the recursive function follows the same logic
private IEnumerable<SubProjectDto> GetActivitiesByEndDateRange(List<SubProjectDto> children, DateTime? endDateFrom, DateTime? endDateTo)
{
var subProjects = new List<SubProjectDto>();
subProjects.AddRange(children);
for (var i = 0; i < subProjects.Count; i++)
{
var acts = new List<ActivityDto>();
acts.AddRange(subProjects[i].Activities);
if (endDateFrom != null)
acts.RemoveAll(activity => activity.EndDate < endDateFrom);
if (endDateTo != null)
acts.RemoveAll(activity => activity.EndDate > endDateTo);
subProjects[i].Activities = acts;
if (subProjects[i].Children.Any())
subProjects[i].Children = GetActivitiesByEndDateRange(subProjects[i].Children.ToList(), endDateFrom, endDateTo);
}
children = subProjects;
return children;
}
Select
,Where
, and other are among them). These methods are often possible to chain effectively, and they lead to code that follows functional style rather than imperative. In my experience, functional code is usually more robust and less error prone compared to imperative. I will try to sketch an answer in 8-10 hours when I get to my desktop. \$\endgroup\$subProjects.AddRange(projects[a].Children);
. Children is not member ofProjectDto
. \$\endgroup\$