LINQ to SQL 递归查询

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

仅使用此表格,如何编写一个使用linq to sql的linq查询来递归获取父数据。

例如,如果选择的雇主ID为4,则应该给出具有ID 4、3、1的员工列表。

谢谢。


4,3,1?? 你在说什么算法呢? :) 如果我选择5会发生什么? - Serkan Hekimoglu
@Serkan:这应该会给出一个员工ID为5、2的列表。 - stackoverflowuser
我还是不明白你的意思。2 是 managerId 吗?如果是的话,按照我的理解,你想要选择 EmployeeId 和这个 EmployeeId 的 managerId 作为 EmployeeId? - Serkan Hekimoglu
@Serkan:它必须递归地获取父级数据。因此,如果4是所选的员工ID,它将选择3(4的经理ID),然后选择1(3的经理ID)。由于1没有经理ID,它将停止并返回4、3和1。 - stackoverflowuser
4个回答

9
这个 .AsHierarchy() 扩展方法可能很有用:链接。然而,它只会提供一种将结果转换为链接对象的简单方法。为了实现这一点,它只会获取所有记录并运行本地递归查询。
如果你正在寻找可通过LINQ to SQL直接翻译为递归SQL查询的LINQ查询,那么你将无法找到。对于最佳性能,存储过程中的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,你可以使用以下脚本进行测试:

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
var managedEmployees = ctx.Employess.Where(x => x.ManagerId = 4).AsEnumerable()

如果你想一次获取整个树,解决方案会更加复杂。在SQL中,最好使用公用表表达式(CTE)来实现这一点,我不知道EF是否能够使用linq处理这个问题-更可能会使用迭代的解决方案。


0
你可以尝试像这样做:
    int id = 5;
    do
    {
        employee= employeedata.FirstOrDefault(e => e.EmployeeId == id);

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

但这是一件相当危险的事情,因为它可能会陷入无限循环。据我所知,除非编写存储过程,否则没有直接进行递归查询的方法。


1
是的,我知道可以通过编写存储过程中的CTE来完成。但我想知道是否有一种直接通过LINQ到SQL表达式实现的方法。似乎答案趋向于“不行”。 - stackoverflowuser
嗯 :) 我没有听到可以使递归的表达式。我以为你在问如何通过编程实现 :) - Serkan Hekimoglu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接