Рефакторинг кода формирования дерева из бд

У меня есть код получения отделов и сотрудников, надо вывести дерево. В целом код работает, но я бы хотел избавиться от параметра nested без которого я ухожу в бесконечный цикл, как это можно сделать? Сразу оговорюсь что мне не нужны сторонние библиотеки которые умеют работать с деревьями в бд! Если id = null значит отдел главный. Циклов в отделах нет. Еще в модели Department нет коллекции подотделов, есть только id родителя

//nested - костыль для того чтобы не уйти в беск цикл
//если true - то надо доставать по parentId, иначе по id.
private async Task<List<DepartmentTreeItem>> CreateTreeAsync(int? id, bool nested)
{
    var queryableDepartment = (await _departmentRepository.GetQueryableAsync())
        .OrderBy(x => x.Name);

    var employeeQueryable = (await _employeeRepository.GetQueryableAsync())
        .OrderBy(x => x.FullName);

    List<Department> list = new List<Department>();

    if (id != null && !nested)
    {
        list = queryableDepartment
            .Where(x => x.Id == id)
            .ToList();
    }
    else
    {
        list = queryableDepartment
            .Where(x => x.ParentDepartmentId == id)
            .ToList();
    }

    List<DepartmentTreeItem> result = new List<DepartmentTreeItem>();
    foreach (var item in list)
    {
        List<EmployeeItem> employees = new List<EmployeeItem>();

        employees = employeeQueryable.Where(x => x.DepartmentId == item.Id)
            .Select(x => new EmployeeItem(x.Id, x.FullName))
            .ToList();

        var child = new DepartmentTreeItem
        {
            Id = item.Id,
            Name = item.Name,
            Employees = new Collection<EmployeeItem>(employees)
        };
        var manager = child.Employees.FirstOrDefault(x => x.Id == item.ManagerId);
        if (manager != null)
        {
            child.Employees.Remove(manager);
            child.Manager = manager;
        }
        var items = await CreateTreeAsync(child.Id, true);
        result.Add(child);
        child.Departments = new Collection<DepartmentTreeItem>(items);
    }

    return result;
}

Ответы (1 шт):

Автор решения: tym32167

Я бы предложил просто вычитать из БД все данные и по ним сразу всё построить, без рекурсий и прочих сложностей.

Раз вы не привели модели, я их придумал как то так

class Department
{
    public int Id;
    public string Name;
    public int ParentDepartmentId;
}

class Employee
{
    public int Id;
    public string FullName;
    public int DepartmentId ;
}

class DepartmentTreeItem
{
    public int Id;
    public string Name;
    public List<DepartmentTreeItem> Departments;
    public List<EmployeeItem> Employees;
}

class EmployeeItem
{
    public int Id;
    public string FullName;
}

Функция, которая получает данные уже вычитанные из БД и компонует дерево

private List<DepartmentTreeItem> CreateTree(List<Department> deps, List<Employee> empl)
{
    // делаем список департаментов в дерево
    var treeDepartments = deps.Select(d => new DepartmentTreeItem() {
        Id = d.Id,
        Name = d.Name,
        Employees = empl
            .Where(e => e.DepartmentId == d.Id).Select(e => new EmployeeItem() {Id = e.Id, FullName = e.FullName})
            .ToList()
    }).ToList();
    
    // проставляем департаментам дочерние департаменты
    foreach(var group in deps.GroupBy(d=>d.ParentDepartmentId))
    {
        var tree = treeDepartments.Where(x=>x.Id == group.Key).Single();
        tree.Departments = group
            .Select(x=>treeDepartments.Where(z=>z.Id == x.Id).Single())
            .ToList();
    }
    
    return treeDepartments;
}

Этот код вернет все отделы списком. Если вам надо только верхник узлы вернуть, добавьте фильтрацию в конце метода, тут

    return treeDepartments; // замените тут на нужную вам фильтрацию
→ Ссылка