LINQ to SQL рекурсивный запрос - PullRequest
16 голосов
/ 01 ноября 2010
EmployeeId  Name  ManagerId
------------------------------
1           A     null
2           B     null
3           C     1
4           D     3
5           E     2

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

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

Спасибо.

Ответы [ 4 ]

7 голосов
/ 02 ноября 2010

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

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

2 голосов
/ 02 ноября 2010

Я не уверен, что это именно то, что вам нужно, но вот один рекурсивный метод, который использует некоторый 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 , вы можете проверить это с помощью следующего скрипта:

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 [] { };
    }
}
0 голосов
/ 01 ноября 2010

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

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

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

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

0 голосов
/ 01 ноября 2010
var managedEmployees = ctx.Employess.Where(x => x.ManagerId = 4).AsEnumerable()

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...