Рекурсивный запрос linq to sql

EmployeeId  Name  ManagerId
------------------------------
1           A     null
2           B     null
3           C     1
4           D     3
5           E     2

просто используя эту таблицу, как можно записать запрос linq (используя linq to sql) для рекурсивного извлечения родительских данных.

Например, если выбран идентификатор работодателя 4, он должен предоставить список сотрудников с идентификаторами: 4, 3, 1

Спасибо.

Ответы

Ответ 1

Этот метод расширения .AsHierarchy() может оказаться полезным: ссылка. Тем не менее, это только работает, предоставляя простой способ передать результаты в связанные объекты. Чтобы сделать это, он просто получит все записи и запустит свой локальный рекурсивный запрос.

Если вы ищете запрос LINQ, который будет напрямую переводить на рекурсивный SQL-запрос через LINQ to SQL, вы его не найдете. Для лучшей производительности CTE в хранимой процедуре, вероятно, вы ищете. Если у вас действительно простая страница, которая все равно должна загружать все дерево, метод AsHierarchy, вероятно, будет соответствовать вашим потребностям.

Ответ 2

Я не уверен, что это именно то, что вы хотите, но вот один рекурсивный метод, который использует некоторый linq, который не должен входить в бесконечный цикл:

    public static IEnumerable<Employee> GetTreeForEmployeeNumber(this IEnumerable<Employee> source, int startingId) {
        var result = source.Where(x => x.EmployeeId == startingId).FirstOrDefault();
        if (result != null) {
            var resultAsE = new [] { result };
            if (!result.ManagerId.HasValue)
                return resultAsE;
            return resultAsE.Union(source.Except(resultAsE).GetTreeForEmployeeNumber(result.ManagerId.Value));
        }
        return new Employee [] { };
    }

Если у вас установлен linqpad, вы можете протестировать его со следующим script:

void Main()
{
    var lst = new [] {
        new Extensions.Employee{ EmployeeId = 1, Name = "A", ManagerId = null }, 
        new Extensions.Employee{ EmployeeId = 2, Name = "B", ManagerId = null }, 
        new Extensions.Employee{ EmployeeId = 3, Name = "C", ManagerId = 1 }, 
        new Extensions.Employee{ EmployeeId = 4, Name = "D", ManagerId = 3 }, 
        new Extensions.Employee{ EmployeeId = 5, Name = "E", ManagerId = 2 }
    };

    lst.GetTreeForEmployeeNumber(4).Dump();
}

public static class Extensions {

    public class Employee {
        public int EmployeeId { get; set; }
        public string Name { get; set; }
        public int? ManagerId { get; set; }
    }

    public static IEnumerable<Employee> GetTreeForEmployeeNumber(this IEnumerable<Employee> source, int startingId) {
        var result = source.Where(x => x.EmployeeId == startingId).FirstOrDefault();
        if (result != null) {
            var resultAsE = new [] { result };
            if (!result.ManagerId.HasValue)
                return resultAsE;
            return resultAsE.Union(source.Except(resultAsE).GetTreeForEmployeeNumber(result.ManagerId.Value));
        }
        return new Employee [] { };
    }
}

Ответ 3

var managedEmployees = ctx.Employess.Where(x => x.ManagerId = 4).AsEnumerable()

Если вам нужно сразу все дерево, решение будет более сложным. В SQL лучше всего это делается с помощью CTE, я не знаю, может ли EF справиться с этим с помощью linq - скорее, будет использоваться итерационное решение.

Ответ 4

Вы можете сделать что-то вроде

    int id = 5;
    do
    {
        employee= employeedata.FirstOrDefault(e => e.EmployeeId == id);

    } while (employee != null && (id = employee.ManagerId) != 0);

но это довольно опасная вещь, поскольку она может застрять в бесконечном цикле. Насколько я знаю, нет никакого способа сделать рекурсивный запрос напрямую, если вы не напишите хранимую процедуру.