/// <summary> /// Helper function used to help finding the employee within the collection (by their ID) /// </summary> /// <param name="employeeTOs">The Employee collection</param> /// <param name="id">The employee-id to search</param> /// <returns></returns> private EmployeeTO FindEmployeeTO(IEnumerable <EmployeeTO> employeeTOs, int id) { EmployeeTO output = employeeTOs.FirstOrDefault(x => x.Id == id); if (output == null) { foreach (var employeeTO in employeeTOs) { output = FindEmployeeTO(employeeTO.Subordinates, id); if (output != null) { break; } } } return(output); }
/// <summary> /// The real handler's implementation for this query /// </summary> /// <param name="query">The input-query object</param> /// <returns>The output object that will contain the hierarchical employee data</returns> public GetEmployeeHierarchyQueryOutput Handle(GetEmployeeHierarchyQuery query) { #region Init var output = new GetEmployeeHierarchyQueryOutput(); IQueryable <Infrastructure.Repository.Models.Employee> employees = null; var dictEmployees = new Dictionary <int, EmployeeTO>(); var dictPendingManagerEmployees = new Dictionary <int, List <EmployeeTO> >(); var rootEmployeeTOs = new List <EmployeeTO>(); #endregion #region Retrieve all employees try { if (output.IsSuccess) { employees = _employeeRepo.All(); } } catch (Exception ex) { var message = $"Error occured when trying to gather all employees data : {ex.Message}"; output.Fail(message); _logger.Error(ex, message); } #endregion #region Processing individual employee if (output.IsSuccess) { foreach (var employee in employees) { if (output.IsSuccess) { try { // Check if this employee has already been processed // Any duplicate ID will be skipped if (!dictEmployees.ContainsKey(employee.Id)) { // Populate the transfer-object from the retrieved-entity var employeeTO = new EmployeeTO { Id = employee.Id, Name = employee.Name, ManagerId = employee.ManagerId }; // Add this new employee to the 'dictEmployees' dictEmployees.Add(employee.Id, employeeTO); // Check if whether the employee has manager-id if (!employeeTO.ManagerId.HasValue) { // This employee doesn't have any manager set against him/her // so, we will add it to the root-employee rootEmployeeTOs.Add(employeeTO); } else { // This employee has any manager set against him/her // Check if this employee's manager has already been added to the employee-dictionary EmployeeTO managerTO = null; if (dictEmployees.TryGetValue(employee.ManagerId.Value, out managerTO)) { // We find the manager of this employee, so we will establish the link between the two // i.e. adding this employee to the manager's subordinate list managerTO.AddSubordinate(employeeTO); } else { // We cannot find the manager for this employeee, // therefore we will just add it into dictPendingManagerEmployees, // the dictionary that keep track of all 'pending' manager // i.e. the manager-id that hasn't been found in the rettrieved-employee data // they 'key' will be the manager-id, // and the 'value' will be the list of employee(subordinates) // This list will serve as "Temporary" list // to hold the list of employee(subordinates) // that will be used in the following block List <EmployeeTO> currentPendingManagerEmployees; // First, we'll check if this manager-id has been added to the dictionary // e.g. by other employees (subordinate) reporting to this manager if (dictPendingManagerEmployees.TryGetValue(employeeTO.ManagerId.Value, out currentPendingManagerEmployees)) { // Yes, this manager-id has been added before // Therefore, we'll just add the currently-processed employee to the 'currentPendingManagerEmployees currentPendingManagerEmployees.Add(employeeTO); } else { // Nope, this manager-id has NOT been added before // Therefore we'll initialize the list of subordinate, and also adding the currently processed employee into it dictPendingManagerEmployees.Add(employeeTO.ManagerId.Value, new List <EmployeeTO> { employeeTO }); } } } // Recall that previously we add the list of 'unknown-yet' manager-id to the 'dictPendingManagerEmployees' // Here is now the chance for us to see if any of the entry matching the currently-processed employee-id // or, in another word, is this employee the manager of any previously processed employees (whose emanager-id cannot be identified before) ? List <EmployeeTO> pendingManagerEmployees; if (dictPendingManagerEmployees.TryGetValue(employeeTO.Id, out pendingManagerEmployees)) { // Yeyy, we found a match here // Assign this employee as the manager of these associated pending-employee-with-no-manager list // which is stored in the 'value' part of the 'dictPendingManagerEmployees' pendingManagerEmployees.ForEach(x => employeeTO.AddSubordinate(x)); // Once processed, we'll remove this employee from the dictPendingManagerEmployees dictPendingManagerEmployees.Remove(employeeTO.Id); } } } catch (Exception ex) { var message = $"Error occured when trying to process employee data ({employee.Name} - ID {employee.Id}) : {ex.Message}"; output.Fail(message); _logger.Error(ex, message); // exit the loop once an error occured break; } } } } #endregion #region Finalize checking on any "pending" employee whose manager-id hasn't been found yet if (output.IsSuccess) { var currentlyProcessedManagerId = -1; try { // Final check if there is any remaining unknown-manager-id // in which, at this stage, we will regard it as 'invalid' // we will just create an "(Unknown)" entry foreach (var pendingManagerEmployees in dictPendingManagerEmployees) { currentlyProcessedManagerId = pendingManagerEmployees.Key; var unknownManagerTO = new EmployeeTO { Id = pendingManagerEmployees.Key, Name = "(Unknown)", Subordinates = pendingManagerEmployees.Value }; rootEmployeeTOs.Add(unknownManagerTO); } // Clear the dictionary as we are no longer using it dictPendingManagerEmployees.Clear(); } catch (Exception ex) { var message = $"Error occured when trying to finalize checking on any 'pending' employee whose manager-id hasn't been found yet : {ex.Message} // currenty-processed-manager-id : {currentlyProcessedManagerId}"; output.Fail(ex.Message); _logger.Error(ex, message); } } #endregion #region Finalize the output object if (output.IsSuccess) { output.RootEmployees = rootEmployeeTOs; } return(output); #endregion }