【问题标题】:linq to sql recursive querylinq to sql递归查询
【发布时间】:2010-11-01 19:00:26
【问题描述】:
EmployeeId  Name  ManagerId
------------------------------
1           A     null
2           B     null
3           C     1
4           D     3
5           E     2

仅仅使用这张表,如何编写一个linq查询(使用linq to sql)递归地获取父数据。

例如,如果选择的 Employer Id 为 4,则应给出 ID 为:4、3、1 的员工列表

谢谢。

【问题讨论】:

  • 4,3,1??你说的是什么算法? :) 如果我选择 5 会发生什么?
  • @Serkan:这应该给出一个 ID 为 5、2 的员工列表
  • 我还是听不懂你。 2 是 managerId?如果是的话,据我所知。您希望选择的 EmployeeId 和这个 EmployeeId 的 managerId 作为 EmployeeId 吗?
  • @Serkan:它必须递归地获取父数据。因此,如果 4 是选定的员工 id,它将去选择 3(4 的经理 ID),然后去选择 1(3 的经理 ID)。由于 1 没有经理 ID,它将停止并返回 4,3 和 1。

标签: c# linq linq-to-sql


【解决方案1】:

这个 .AsHierarchy() 扩展方法可能有用:link。但是,这只能通过提供一种简单的方法将结果放入链接对象中来实现。为此,它只会获取所有记录并运行自己的本地递归查询。

如果您正在寻找可以通过 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 [] { };
        }
    }
    

    【讨论】:

      【解决方案3】:

      你可以做类似的事情

          int id = 5;
          do
          {
              employee= employeedata.FirstOrDefault(e => e.EmployeeId == id);
      
          } while (employee != null && (id = employee.ManagerId) != 0);
      

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

      【讨论】:

      • 是的,我知道可以通过在存储过程中编写 CTE 来完成。我想看看是否有一种方法可以直接通过 linq to sql 表达式来实现。似乎答案正朝着“不”的方向发展。
      • hmm :) 我没有听到可以递归的表达式。我以为你问如何以编程方式制作:)
      【解决方案4】:
      var managedEmployees = ctx.Employess.Where(x => x.ManagerId = 4).AsEnumerable()
      

      如果您想同时获得整棵树,则解决方案会更复杂。在 SQL 中,最好使用 CTE 完成,我不知道 EF 是否可以使用 linq 来处理这个问题——更有可能使用迭代解决方案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-12-17
        • 1970-01-01
        • 1970-01-01
        • 2012-01-29
        • 2017-08-13
        • 1970-01-01
        • 2010-10-06
        相关资源
        最近更新 更多