I'm creating a filter with Linq in an ASP.NET MVC application. I was hoping I could get some tips on performance enhancements, aside from any possible performance enhancements.
I have a few questions:
- Should I
ToListbefore my extension methods that use .Select? - Would adding
AsNoTrackinghelp performance? If so, if myGetAllIncludingmethod returnsIEnumerable, where should I addAsQueriable().AsNoTracking()? - The Entities
SubProjectandActivityare self referencing Entities I'm trying to eager load them as all the lazy loading looping is extremely slow. Am I doing this in the correct manner?
I know this is using Repo pattern and UOW, but that's just the way this app was built so I would like to build this around that.
Here is the DTO I'm sending to the client:
public class GanttDto
{
public IEnumerable<ProductLineDto> ProductLines { get; set; }
public IEnumerable<ProjectDto> Projects { get; set; }
public IEnumerable<SubProjectDto> Subprojects { get; set; }
public IEnumerable<ActivityDto> Activities { get; set; }
public IEnumerable<ProjectTypeDto> ProjectTypes { get; set; }
}
Here are my filter methods
public GanttDto Filter(GanttFilterDto filterCriteria)
{
FilterCriteria = filterCriteria;
var projects = FilterProjects().ToList();
var pIds = projects.Select(x => x.ProjectID);
var data = new GanttDto {
ProductLines = FilterProductLines(),
Projects = projects.ToProjectDtoEnumerable(),
Subprojects = FilterSubProjects(pIds),
Activities = GetActivities(pIds),
ProjectTypes = UnitOfWork.ProjectTypeRepository.GetAll().OrderBy(z => z.SortOrder).ToList().ToProjectTypeDtoEnumerable()
};
return data;
}
Here is the GetAllIncluding method I'm using for each method below:
public IEnumerable<TEntity> GetAllIncluding(params Expression<Func<TEntity, object>>[] includesProperties)
{
return includesProperties.Aggregate<Expression<Func<TEntity, object>>,
IQueryable<TEntity>>(_dbSet, (current, includeProperty)
=> current.Include(includeProperty));
}
Filter projects
private IEnumerable<Project> FilterProjects()
{
var productLineIds = FilterCriteria.SelectedProjects.Where(x => x.ProductLineId != null).Select(x => x.ProductLineId).ToList();
var projectTypeIds = FilterCriteria.SelectedProjects.Where(x => x.ProjectTypeId != null).Select(x => x.ProjectTypeId).ToList();
var projectIds = FilterCriteria.SelectedProjects.Where(x => x.ProjectId != null).Select(x => x.ProjectId).ToList();
return UnitOfWork.ProjectRepository.GetAllIncluding(x => x.ProjectPersons,
x => x.ProjectPersons.Select(y => y.ProjectPersonRoles))
.Where(prj => ((productLineIds.Count == 0 || productLineIds.Any(id => id == prj.ProductLineID)) //filter by ProductLine
&& (projectTypeIds.Count == 0 || projectTypeIds.Any(id => id == prj.ProjectTypeID)) // ProjectType
&& (projectIds.Count == 0 || projectIds.Any(id => id == prj.ProjectID)) // Project
&& (FilterCriteria.StatusId.Contains(prj.ProjectStatusTypeID)) //project status
)).ToList();
}
To Project Dto
public static IEnumerable<ProjectDto> ToProjectDtoEnumerable(this IEnumerable<Project> projects)
{
return projects.Select(x => new ProjectDto
{
ProjectId = x.ProjectID,
ProjectName = x.Name,
StartDate = x.StartDate,
//Children = x.SubProjects.Where(y => y.ParentSubProjectID == null).ToSubProjectDtoEnumerable(),
Organization = x.Organization.ToOrganizationDto(),
ProjectStatus = x.ProjectStatusType.Description,
ProjectTypeRowId = x.ProjectType.RowID,
ProjectTypeDescription = x.ProjectType.Description,
SortOrder = x.SortOrder,
ProjectPersons = x.ProjectPersons.ToProjectPersonDtoEnuemrable(),
ProjectStatusId = x.ProjectStatusTypeID,
ProductLineId = x.ProductLineID,
ProjectTypeId = x.ProjectTypeID,
OrganizationId = x.OrganizationID,
ProductLineName = x.ProductLine.Description,
IsShotgun = x.IsShotgun,
ShotgunLink = x.ShotgunLink,
Description = x.Description,
Flow = x.Flow,
ModelType = x.ModelType,
ModelYear = x.ModelYear,
BudgetCode = x.BudgetCode,
ProjectCode = x.ProjectCode,
ProjectLeaderDescription = x.ProjectLeaderDescription
});
}
Filter ProductLines
private IEnumerable<ProductLineDto> FilterProductLines() {
var productLineIds = FilterCriteria.SelectedProjects.Where(x => x.ProductLineId != null).Select(x => x.ProductLineId).ToList();
return UnitOfWork.ProductLineRepository.Where(x => productLineIds.Count == 0 || productLineIds.Contains(x.ProductLineID)).OrderBy(z => z.SortOrder).ToList().ToProductLineDtoEnumerable();
}
To ProductLine Dto
public static IEnumerable<ProductLineDto> ToProductLineDtoEnumerable(this IEnumerable<ProductLine> productLines)
{
return productLines.Select(x => new ProductLineDto
{
Name = x.Description,
ProductLineId = x.ProductLineID,
SortOrder = x.SortOrder
});
}
Filter SubProjects
private IEnumerable<SubProjectDto> FilterSubProjects(IEnumerable<Guid> projectIds)
{
var sp = UnitOfWork.SubProjectRepository.GetAllIncluding(x => x.SubProject1,
x => x.SubProjectPersons,
x => x.SubProjectPersons.Select(y => y.SubProjectPersonRoles),
x => x.SubProjectTeams)
.Where(y => y.ParentSubProjectID == null && projectIds.Contains(y.ProjectID)).OrderBy(z => z.SortOrder).ToList();
return sp.ToSubProjectDtoEnumerable();
}
To SubProjectDto
public static IEnumerable<SubProjectDto> ToSubProjectDtoEnumerable(this IEnumerable<SubProject> projects)
{
return projects.Select(x => new SubProjectDto
{
ProjectId = x.ProjectID,
SubProjectId = x.SubProjectID,
ProjectName = x.Name,
Children = x.SubProject1.Where(y => y.ParentSubProjectID == x.SubProjectID).ToSubProjectDtoEnumerable(),
ParentId = x.ParentSubProjectID,
SubProjectPersons = x.SubProjectPersons.ToSubProjectPersonDtoEnuemrable(),
SubProjectTypeId = x.SubProjectTypeID,
OrganizationId = x.OrganizationID,
SortOrder = x.SortOrder,
IsShotgun = x.IsShotgun,
ExpandToCustomLevel = x.expandToCustomLevel,
Teams = x.SubProjectTeams?.ToSubProjectTeamDtoEnumerable()
});
}
Filter Activities
private IEnumerable<ActivityDto> GetActivities(IEnumerable<Guid> projectIds) {
if (FilterCriteria.ActivityTypeId == null)
FilterCriteria.ActivityTypeId = new List<Guid>();
if (FilterCriteria.MileStoneTypeId == null)
FilterCriteria.MileStoneTypeId = new List<Guid>();
var acts = UnitOfWork.ActivityRepository.GetAllIncluding(
x => x.ActivityTypeCustomForms,
x => x.Activity1,
x => x.ActivityPersons,
x => x.ActivityMachines,
x => x.ActivityType
).Where(act =>
(FilterCriteria.MileStoneTypeId.Contains(act.ActivityTypeID))
||
(act.ParentActivityID == null && projectIds.Contains(act.ProjectID.Value)) //by project
&&// start date - if start date is between start and end get them too
(FilterCriteria.StartDateFrom == null || (((act.EndDate >= FilterCriteria.StartDateFrom) && (act.StartDate <= FilterCriteria.StartDateFrom)) ||
((act.StartDate >= FilterCriteria.StartDateFrom && act.EndDate <= FilterCriteria.StartDateFrom)) ||
(act.StartDate >= FilterCriteria.StartDateFrom)))
&&// End date - if End date is between start and end get them too
(FilterCriteria.EndDateFrom == null || (((act.EndDate >= FilterCriteria.EndDateFrom) && (act.StartDate <= FilterCriteria.EndDateFrom)) ||
((act.StartDate >= FilterCriteria.EndDateFrom && act.EndDate <= FilterCriteria.EndDateFrom)) ||
(act.EndDate <= FilterCriteria.EndDateFrom)))
&& //activity type
(FilterCriteria.ActivityTypeId.Contains(act.ActivityTypeID))
&&//resource type
(FilterCriteria.ResourceTypeIds == null || act.ActivityPersons.Any(ap => FilterCriteria.ResourceTypeIds.Contains(ap.ResourceTypeID.Value)))
&&//PersonID
(FilterCriteria.PersonId == null || act.ActivityPersons.Any(ap => (ap.PersonID != null && FilterCriteria.PersonId.Contains(ap.PersonID.Value))))
).OrderBy(z => z.SortOrder).ToList().ToActivityDtoEnumerable();
return acts;
}
To Activity Dto
public static IEnumerable<ActivityDto> ToActivityDtoEnumerable(this IEnumerable<Activity> activities)
{
return activities.Select(x => new ActivityDto
{
ActivityId = x.ActivityID,
ActivityName = x.Name,
Description = x.SpecialDescription,
ActivityType = x.ActivityType.ToActivityTypeDto(),
ActivityDateRange = new DateRange(x.StartDate, x.EndDate),
StartDate = x.StartDate,
EndDate = x.EndDate,
Duration = x.Duration,
PctComplete = x.PctComplete,
Name = x.Name,
ActivityPersons = x.ActivityPersons.ToActivityPersonDtoEnumerable(),
ActivityMachines = x.ActivityMachines.ToActivityMachineDtoEnumerable(),
Organization = x.Organization.ToOrganizationDto(),
OrganizationId = x.OrganizationID,
SortOrder = x.SortOrder,
ProjectName = x.Project == null ? "Not Found" : x.Project.Name,
PlannedStart = x.EarliestStartDate,
PlannedEnd = x.EarliestEndDate,
LaserProcessId = x.LaserProcessID,
LaserThicknessId = x.LaserThicknessID,
MaterialId = x.MaterialID,
MillingMinimumRadiusId = x.MillingMinimumRadiusID,
MillingScallopHeightId = x.MillingScallopHeightID,
ModelCategoryId = x.ModelCategoryID,
ModelTypeId = x.ModelTypeID,
RpfdmNozzleId = x.RPFDMNozzleID,
RpfillTypeId = x.RPFillTypeID,
RpsparseId = x.RPSparseID,
VarianceId = x.VarianceID,
QuantityId = x.QuantityID,
SpecNotes = x.SpecNotes,
Children = x.Activity1.ToActivityDtoEnumerable(),
CustomForm = x.ActivityTypeCustomForms.Any() ? x.ActivityTypeCustomForms.ToActivityCustomFormDto() : new List<ActivityTypeCustomFormDto>(),
PartsDetailsDescription = x.PartsDetailsDescription,
});
}
IEnumerable.Contains()but usingHashSetorDictionarywill reduce number of loops. I would change the 3 vars by HashSet since they are all list ofnativevalues (Ids) \$\endgroup\$