The following code snippet is causing me a performance problem in my application. The first 5 db queries are not high time consuming, the real problem lays on the foreach loop. I considered to use Dictionary structures instead of List to improve the lookups, but improving the loop will make the real difference.
public void ProcessShifts(IList<NavTechAbsenceDto> navShifts)
{
var branches = _iagiBranchModelFactory.GetAll().ToList();
var technicians = _iagiTechnicianModelFactory.GetAll().Where(it => it.IsActive).ToList();
var activeTechCodes = technicians.Select(t => t.ERPId).ToList();
var absencesForActiveTech = navShifts.Where(a => activeTechCodes.Contains(a.TechCode)).ToList();
var absences = _iagiTechnicianModelFactory.GetAllWithAbsences().SelectMany(it => it.Absences);
IEnumerable<Absence> mwmAbsenceList = absences == null ? null : absences.Where(a => a.Start >= DateTime.Today);
foreach (NavTechAbsenceDto absence in absencesForActiveTech)
{
var mwmAbsence = mwmAbsenceList == null ? null : mwmAbsenceList.FirstOrDefault(it => it.AGIErpId == absence.AGIErpId);
AGIBranchModel branch = branches.FirstOrDefault(b => b.ERPId == absence.BranchCode);
AGITechnicianModel tech = technicians.FirstOrDefault(t => t.ERPId == absence.TechCode);
if (branch == null || tech == null)
{
Logger.Trace("NOT AVAILABLE: Shift Branch: {0}, Shift Technician: {1}", absence.BranchCode, absence.TechCode);
continue;
}
if (mwmAbsence == null)
{
if (absence.AGILogicallyDeleted == 1)
{
Logger.Trace(string.Format("DELETED absence:{0} from Navision, but it does not exist in Scheduler", absence.AGIErpId));
}
else
{
Logger.Trace(string.Format("ADD absence:{0}", absence.AGIErpId));
tech.AddAbsence(absence, branch);
}
}
else
{
if (absence.AGILogicallyDeleted == 1)
{
Logger.Trace(string.Format("DELETE absence:{0}", absence.AGIErpId));
tech.DeleteAbsence(mwmAbsence);
}
else
{
Logger.Trace(string.Format("UPDATE absence:{0}", absence.AGIErpId));
mwmAbsence.Start = absence.Start;
mwmAbsence.End = absence.End;
mwmAbsence.AGIBranchId = branch.Id;
mwmAbsence.TechnicianId = tech.Id;
mwmAbsence.Technician = tech;
mwmAbsence.AGIType = absence.AGIType;
mwmAbsence.AGIMobile = absence.AGIMobile;
mwmAbsence.AGIErpId = absence.AGIErpId;
tech.UpdateAbsence(mwmAbsence);
}
}
}
}
.Include
to load the foreign rows immediately. Alternatively, if that's not possible, you can create a list ofabsence.BranchCode
, queryBranches.Where(b => branchCodes.Contains(b.EPRId))
and then resolve the matches in memory once you've got them all. A fast way would be to put the results in a dictionary \$\endgroup\$