Linq 如何使用实体框架检索父子关系数据并对其进行分页/筛选/排序

Linq 如何使用实体框架检索父子关系数据并对其进行分页/筛选/排序,linq,entity-framework-core,Linq,Entity Framework Core,我有一个自引用的DB表,用于形成父子关系 身份证件 名称 父ID 1. A. 无效的 2. B 1. 3. C 2. 4. D 2. 5. E 1. 这是一个常见的问题,而不是EFCore本机处理的问题 注意事项-您几乎肯定希望添加检查以确保 所有新记录/编辑记录不会无意中创建循环 引用,例如:Temp1=newtemp{Id=1,ParentId=2};Temp2=newtemp{Id=2,ParentId=1}-这将导致加载树时发生不好的事情 首先,不太明显的解决方案是,仅在检索根节点后使用

我有一个自引用的DB表,用于形成父子关系

身份证件 名称 父ID 1. A. 无效的 2. B 1. 3. C 2. 4. D 2. 5. E 1.
这是一个常见的问题,而不是EFCore本机处理的问题

注意事项-您几乎肯定希望添加检查以确保 所有新记录/编辑记录不会无意中创建循环 引用,例如:
Temp1=newtemp{Id=1,ParentId=2};Temp2=newtemp{Id=2,ParentId=1}-这将导致加载树时发生不好的事情

首先,不太明显的解决方案是,仅在检索根节点后使用延迟加载:

// Get a list of root nodes.
// Optionally, add pagination, filtering and sorting to the query - .Where(x => x.Name.Contains("blah")).Skip(10).Take(5).OrderBy(x => x.Sequence) etc.
var rootNodes = dbContext.Temp.Where(x => x.parentId == null).ToList();

// Child nodes will be loaded as you access them.
foreach (var node in rootNodes)
{
    foreach (var child in node.Children) {
        // Do something with children that were lazy-loaded.
    }
}
// For a maximum depth of two (or root, leaf, leaf)
// Optionally, add pagination, filtering and sorting to the query - .Where(x => x.Name.Contains("blah")).Skip(10).Take(5).OrderBy(x => x.Sequence) etc.
var query = dbContext.Temp
    .Where(x => x.ParentId == null)
    .Include(x => x.children)
        .ThenInclude(x => x.children);
有关配置延迟加载的详细信息:

注意:延迟加载可能导致效率低下的行为/意外的数据库 查询。这很方便,但方便是有代价的

如果您的业务规则规定了最大深度,则可以按如下所示使用include语句,请注意过滤器仅检索根节点:

// Get a list of root nodes.
// Optionally, add pagination, filtering and sorting to the query - .Where(x => x.Name.Contains("blah")).Skip(10).Take(5).OrderBy(x => x.Sequence) etc.
var rootNodes = dbContext.Temp.Where(x => x.parentId == null).ToList();

// Child nodes will be loaded as you access them.
foreach (var node in rootNodes)
{
    foreach (var child in node.Children) {
        // Do something with children that were lazy-loaded.
    }
}
// For a maximum depth of two (or root, leaf, leaf)
// Optionally, add pagination, filtering and sorting to the query - .Where(x => x.Name.Contains("blah")).Skip(10).Take(5).OrderBy(x => x.Sequence) etc.
var query = dbContext.Temp
    .Where(x => x.ParentId == null)
    .Include(x => x.children)
        .ThenInclude(x => x.children);
对于深度未知的树,最好是获取整个平面列表并自己构建嵌套(客户端而不是EF查询),请注意,分页必须在结果上:

// My recursive function to load child nodes.
Action<List<Temp>, <Temp>> LoadChildren = delegate(sourceList, loadChildrenFor) {
    // Get the children for the specified node.
    loadChildrenFor.Children = sourceList.Where(x => x.ParentId == loadChildrenFor.Id).ToList();
    // Remove the children from the source list AND load its children.
    foreach(var node in loadChildrenFor.Children)
    {
        sourceList.Remove(node); // Don't need in source list any more, it's a child.
        LoadChildren(sourceList, node);
    }
}

var allNodes = dbContext.Temp.ToList();
foreach (var node in allNodes.Where(x => x.parentId == null))
    LoadChildren(allNodes, node);

// Now allNodes ONLY contains root nodes with all their children populated, // Optionally, add pagination, filtering and sorting to the query - .Where(x => x.Name.Contains("blah")).Skip(10).Take(5).OrderBy(x => x.Sequence) etc.
//加载子节点的递归函数。
Action LoadChildren=委托(sourceList,loadChildrenFor){
//获取指定节点的子节点。
loadChildrenFor.Children=sourceList.Where(x=>x.ParentId==loadChildrenFor.Id).ToList();
//从源列表中删除子项并加载其子项。
foreach(loadChildrenFor.Children中的var节点)
{
sourceList.Remove(node);//不再需要在源列表中,它是一个子项。
LoadChildren(sourceList,node);
}
}
var allNodes=dbContext.Temp.ToList();
foreach(allNodes.Where中的var节点(x=>x.parentId==null))
加载子节点(所有节点,节点);
//现在allNodes只包含根节点及其所有子节点,//可选地,向查询添加分页、筛选和排序-.Where(x=>x.Name.contains(“blah”)).Skip(10)、Take(5)、OrderBy(x=>x.Sequence)等。
对于列表展平的一般实现:

如果树非常大且深度未知,并且您只需要一个子集(或一个分支),则递归函数中的多个查询最合适:

// My recursive function to load child nodes.
Action<Temp> LoadChildren = delegate(ofNode) {
    ofNode.Children = dbContext.Temp.Where(x => x.ParentId == ofNode.Id).ToList();
    foreach(var node in ofNode.Children)
        LoadChildren(node);
}

// A list of root nodes I'm interested in - could be any depth in tree.
// Optionally, add pagination and sorting to the query - .Skip(10).Take(5).OrderBy(x => x.Sequence) etc.
var rootNodesImInterestedIn = dbContext.Temp.Where(x => x.Id == 5).ToList();
// Load the children for each root node I'm interested in.
foreach(var node in rootNodesImInterestedIn)
    LoadChildren(node);
//加载子节点的递归函数。
Action LoadChildren=委托(ofNode){
ofNode.Children=dbContext.Temp.Where(x=>x.ParentId==ofNode.Id.ToList();
foreach(node.Children中的var节点)
加载子节点;
}
//我感兴趣的根节点列表-可以是树中的任意深度。
//或者,将分页和排序添加到查询-.Skip(10).Take(5).OrderBy(x=>x.Sequence)等。
var rootNodesMinterestedin=dbContext.Temp.Where(x=>x.Id==5.ToList();
//加载我感兴趣的每个根节点的子节点。
foreach(rootNodesMinterestedin中的var节点)
加载子节点;

对于EF,只有在指定大量包含项时才可能:
dbContext.Temp.Include(x=>x.Children)。然后Include(x=>x.Children)。然后Include(x=>x.Children)。然后Include(x=>x.Children)