分享 .NET EF6 查询并返回树形结构数据的 2 个思路和具体实现方法

image

前言

树形结构是一种很常见的数据结构,类似于现实生活中的树的结构,具有根节点、父子关系和层级结构。

所谓根节点,就是整个树的起始节点。

节点则是树中的元素,每个节点可以有零个或多个子节点,节点按照层级排列,根节点属于第一层,其子节点属于第二层,以此类推,没有子节点的节点,则称为叶子,是最后一层。

父子关系就是节点之间的关系,每个节点都有父节点。

树形结构的应用非常广泛,例如在数据库中用来表示组织结构、目录结构,还用于实现树状菜单、文件系统等。

树形结构的灵活性和层次性使其成为处理具有层级关系数据的有力工具。

常见的树形结构包括二叉树、平衡树、B树等,它们在各个领域都有不同的应用场景和算法实现。

下面分享 EF6 查询并返回树形结构数据的 2 个思路和具体实现方法。

1. EF 生成数据表的实体类

/// <summary>
/// HTFP14 表实体类
/// </summary>
public partial class HTFP14
{public string COMPHT14 { get; set; }public string ACDEHT14 { get; set; }public string PGRPHT14 { get; set; }public string PKEYHT14 { get; set; }public string DESCHT14 { get; set; }public Nullable<decimal> PVALHT14 { get; set; }public string PSTRHT14 { get; set; }public string GLNOHT14 { get; set; }public string PCDEHT14 { get; set; }public string ATLVHT14 { get; set; }public string USERHT14 { get; set; }public System.DateTime LMDTMHT14 { get; set; }
}

2. 创建用于前端的 ViewModel 类

/// <summary>
/// 主菜单 UI 树形结构 ViewModel 类
/// </summary>
public class MainMenuViewModel
{[Description("菜单层次")]public int MenuLevel { get; set; }[Description("菜单码")]public string MenuCode { get; set; }[Description("菜单名称")]public string MenuName { get; set; }[Description("菜单对外显示名称")]public string MenuLabel{get{return $"{MenuCode} - {MenuName}";}}[Description("父菜单码")]public string ParentMenuCode { get; set; }[Description("菜单URL")]public string MenuUrl { get; set; }[Description("菜单授权用户")]public string MenuUser { get; set; }[Description("是否禁止")]public bool Disabled{get{if (string.IsNullOrEmpty(MenuUser)){return true;}return false;}}[Description("菜单排序")]public decimal MenuOrder { get; set; }[Description("子级菜单")]public IList<MainMenuViewModel> Children { get; set; }
}

3. 数据准备

  1. 获取初级菜单

    /// <summary>
    /// 查询第一级菜单
    /// </summary>
    /// <returns></returns>
    private IQueryable<MainMenuViewModel> GetFirstMenu()
    {var query = from t1 in _dbContext.HTFP14 where t1.PGRPHT14 == "MNGP" select new MainMenuViewModel{MenuCode = t1.PKEYHT14,MenuName = t1.DESCHT14,ParentMenuCode = "",MenuUrl = "",MenuUser = ""};return query;
    }
    
  2. 获取二级菜单

    /// <summary>
    /// 查询第二级菜单
    /// </summary>
    /// <param name="companyCode"></param>
    /// <returns></returns>
    private IQueryable<MainMenuViewModel> GetSecondMenu(string companyCode)
    {var query = from t1 in _dbContext.HTFP14where t1.PGRPHT14 == "MUGP" && t1.COMPHT14 == companyCodeselect new MainMenuViewModel{MenuCode = t1.PKEYHT14,MenuName = t1.DESCHT14,ParentMenuCode = t1.PCDEHT14,MenuUrl = "",MenuUser = ""};return query;
    }
    
  3. 获取三级(最终)菜单

    /// <summary>
    /// 查询第三级(最终)菜单
    /// </summary>
    /// <param name="companyCode"></param>
    /// <returns></returns>
    private IQueryable<MainMenuViewModel> GetThirdMenu(string menuUser, string companyCode)
    {var query = from t1 in _dbContext.HTFP02where t1.COMPHT02 == companyCode && t1.STSHT02 == "A"join t2 in (from t21 in _dbContext.HTFP03 where t21.USRMNHT03==menuUser && t21.COMPHT03==companyCode select t21) on t1.MNUCDHT02 equals t2.MNUCDHT03 into t1_t2from t12 in t1_t2.DefaultIfEmpty()select new MainMenuViewModel{MenuCode = t1.MNUCDHT02,MenuName = t1.DESCHT02,ParentMenuCode = t1.MNUGPHT02,MenuUrl = t1.URLHT02,MenuUser = t12.USRMNHT03};return query;
    }
    
  4. 解释:这样分开查询,简化代码,避免太复杂的 Linq 拼接

方法一

思路:使用 Linq 语法拼接查询,具体步骤如下:

  1. 在数据层用 Linq 拼接写查询方法

    /// <summary>
    /// 查询主菜单树形结构数据
    /// </summary>
    /// <param name="companyCode"></param>
    /// <returns></returns>
    public IQueryable<object> QueryMainMenus(string menuUser, string companyCode)
    {var query1 = GetFirstMenu();var query2 = GetSecondMenu(companyCode);var query3 = GetThirdMenu(menuUser, companyCode);var query = from t1 in query1select new{MenuCode = t1.MenuCode,MenuName = t1.MenuName,ParentMenuCode = t1.ParentMenuCode,MenuUrl = t1.MenuUrl,MenuUser = t1.MenuUser,Children = (from t2 in query2where t2.ParentMenuCode == t1.MenuCodeselect new{MenuCode = t2.MenuCode,MenuName = t2.MenuName,ParentMenuCode = t2.ParentMenuCode,MenuUrl = t2.MenuUrl,MenuUser = t2.MenuUser,Children = (from t3 in query3where t3.ParentMenuCode == t2.MenuCodeselect new{MenuCode = t3.MenuCode,MenuName = t3.MenuName,ParentMenuCode = t3.ParentMenuCode,MenuUrl = t3.MenuUrl,MenuUser = t3.MenuUser})})};return query;
    }
    
  2. 在业务层直接调用方法生成 List 返回给前端

  3. 总结

    逻辑比较简单,有多个菜单可以一直添加下去,但代码会变得很长,所以比较适合事先知道层级并且层级数量不多的场景。

方法二(推荐)

思路:实体类 + 递归方法,具体步骤如下:

  1. 数据层 EF 使用 Union 方法返回整个树形结构数据:

    /// <summary>
    /// 查询主菜单树形结构数据
    /// </summary>
    /// <param name="companyCode"></param>
    /// <returns></returns>
    public IQueryable<MainMenuViewModel> QueryMainMenus(string menuUser, string companyCode)
    {var query1 = GetFirstMenu();var query2 = GetSecondMenu(companyCode);var query3 = GetThirdMenu(menuUser, companyCode);var query = query1.Union(query2).Union(query3);return query;
    }
    
  2. 业务层递归处理并返回集合数据给前端

    public List<MainMenuViewModel> QueryMainMenus(string menuUser, string companyCode)
    {var list = hTFP02Reposition.QueryMainMenus(menuUser, companyCode).ToList();var list2 = GetData(list);return list2;
    }/// <summary>
    /// 处理树形结构数据
    /// </summary>
    /// <param name="source"></param>
    /// <returns></returns>
    private List<MainMenuViewModel> GetData(List<MainMenuViewModel> source)
    {List<MainMenuViewModel> nodes = source.Where(x => x.ParentMenuCode == "").Select(x => x).ToList();foreach (MainMenuViewModel item in nodes){item.Children = GetChildren(source, item);}return nodes;
    }/// <summary>
    /// 递归处理树形结构数据
    /// </summary>
    /// <param name="source"></param>
    /// <param name="node"></param>
    /// <returns></returns>
    private IList<MainMenuViewModel> GetChildren(List<MainMenuViewModel> source, MainMenuViewModel node)
    {IList<MainMenuViewModel> childrens = source.Where(c => c.ParentMenuCode == node.MenuCode).Select(x => x).ToList();foreach (MainMenuViewModel item in childrens){item.Children = GetChildren(source, item);}return childrens;
    }
    
  3. 总结

    代码比较简单,但逻辑相对不如第一种方法好理解,递归方法的性能略逊于第一种方法,但可扩展性比较强,适用于无法事先知道层级数量的树形数据结构。

往期精彩

  1. 分享一个 .NET 通过监听器拦截 EF 消息写日志的详细例子
  2. 不会使用 EF Core 的 Code First 模式?来看看这篇文章,手把手地教你
  3. EF Core 性能很差?试试这 6 个小技巧
  4. 如何在 EF Core 中使用乐观并发控制
  5. EF Core 在实际开发中,如何分层?

我是老杨,一个奋斗在一线的资深研发老鸟,让我们一起聊聊技术,聊聊程序人生,共同学习,共同进步

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3247535.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

STM32 IAP 需要关注的一些事

1、首先要知道STM32的程序是如何分布在FLASH中的。 2、升级的时候涉及到两个程序&#xff0c;一个是bootloader&#xff0c;一个是user程序&#xff0c;这两个程序的功能分别的什么作用的&#xff1f; 3、编译的固件是怎么分布的&#xff1f;通过那个配置文件去指导编译器去排布…

内网对抗-隧道技术篇防火墙组策略ICMPDNSSMB协议出网判断C2上线解决方案

知识点&#xff1a; 1、隧道技术篇-网络层-ICMP协议-判断&封装&建立&穿透 2、隧道技术篇-传输层-DNS协议-判断&封装&建立&穿透 3、隧道技术篇-表示层-SMB协议-判断&封装&建立&穿透0、不是有互联网才叫出网 1、C2常见上线采用的协议 2、常…

IDEA 调试 Ja-Netfilter

首先本地需要有两款IDEA 可以是相同版本&#xff0c;也可以是不同版本。反正要有两个&#xff0c;一个用来调试代码&#xff0c;一个启动。 移除原有ja-netfiler 打开你的ja-netfiler的vmoptions目录&#xff0c;修改其中的idea.vmoptions文件。移除最后一行-javaagent ...参…

基于R语言BIOMOD2 及机器学习方法的物种分布模拟

BIOMOD2是一个R软件包&#xff0c;用于构建和评估物种分布模型&#xff08;SDMs&#xff09;。它集成了多种统计和机器学习方法&#xff0c;如GLM、GAM、SVM等&#xff0c;允许用户预测和分析物种在不同环境条件下的地理分布。通过这种方式&#xff0c;BIOMOD帮助研究者评估气候…

数据结构(Java):力扣 二叉树面试OJ题(二)【进阶】

目录 &#x1f48e; 1、题一&#xff1a;二叉树的层序遍历 &#x1f31f; 1.1 思路1&#xff08;递归求解&#xff09; &#x1f31f; 1.1.1 思路1代码 &#x1f506; 1.2 思路2&#xff08;队列求解&#xff09; &#x1f506; 1.2.1 思路2代码 &#x1f48e; 2、题二&…

基于Java中的SSM框架实现求职招聘网站系统项目【项目源码】

基于Java中的SSM框架实现线求职招聘网站系统演示 研究方法 本文的研究方法主要有&#xff1a; &#xff08;1&#xff09;调查法 调查法就是在系统的构思阶段&#xff0c;设计者对系统的功能和系统的现状有些不了解&#xff0c;需要去实地的去和本系统相关的区域进行调查&am…

制造运营管理系统(MOM系统),企业实现先进制造的关键一步

随着全球制造业的快速发展&#xff0c;企业对于生产效率和成本控制的要求日益增高。在这个背景下&#xff0c;制造运营管理系统&#xff08;MOM系统&#xff09;成为了企业提升竞争力的关键工具。盘古信息作为业内领先的智能制造解决方案提供商&#xff0c;其MOM系统更是以其卓…

django学习入门系列之第四点《BootStrap依赖》

文章目录 往期回顾 BootStrap依赖于JavaScript的类库&#xff0c;JQuery下载 下载JQuery&#xff0c;在界面上应用JQuery 在页面上应用BootStrap和avaScript的类库【JQuery是avaScript的类库】 JQuery的官网&#xff1a; jQuery 如果要应用JQuery 则要在body里面导入文件…

华为HCIP Datacom H12-821 卷42

42.填空题 如图所示&#xff0c;MSTP网络中SW1为总根&#xff0c;请将以下交换机与IST域根和主桥配对。 参考答案&#xff1a;主桥1468 既是IST域根又是主桥468 既不是又不是就是25 解析&#xff1a; 主桥1468 既是IST域根又是主桥468 既不是又不是就是25 43.填空题 网络有…

【漏洞复现】泛微OA E-Cology getdata.jsp SQL注入漏洞

免责声明&#xff1a; 本文内容旨在提供有关特定漏洞或安全漏洞的信息&#xff0c;以帮助用户更好地了解可能存在的风险。公布此类信息的目的在于促进网络安全意识和技术进步&#xff0c;并非出于任何恶意目的。阅读者应该明白&#xff0c;在利用本文提到的漏洞信息或进行相关测…

Spring Boot集成kudu快速入门Demo

1.什么是kudu 在Kudu出现前&#xff0c;由于传统存储系统的局限性&#xff0c;对于数据的快速输入和分析还没有一个完美的解决方案&#xff0c;要么以缓慢的数据输入为代价实现快速分析&#xff0c;要么以缓慢的分析为代价实现数据快速输入。随着快速输入和分析场景越来越多&a…

【VScode】安装【ESP-IDF】插件及相关工具链

一、ESP-IDF简介 二、VScode安装ESP-IDF插件 三、安装ESP-IDF、ESP-IDF-Tools以及相关工具链 四、测试例程&编译烧录 一、ESP-IDF简介 二、VScode安装ESP-IDF插件 【VScode】安装配置、插件及远程SSH连接 【VSCode】自定义配置 打开VScode&#xff0c;在插件管理搜索esp…

视频共享融合赋能平台LntonCVS视频监控业务平台技术方案详细介绍

LntonCVS国标视频综合管理平台是一款智慧物联应用平台&#xff0c;核心技术基于视频流媒体&#xff0c;采用分布式和负载均衡技术开发&#xff0c;提供广泛兼容、安全可靠、开放共享的视频综合服务。该平台功能丰富&#xff0c;包括视频直播、录像、回放、检索、云存储、告警上…

水利行业的智慧革命:深度剖析智慧水利解决方案,看其如何以科技力量提升水资源管理效率,保障水生态安全

目录 一、智慧水利的概念与内涵 二、智慧水利解决方案的核心要素 1. 感知层&#xff1a;全面监测&#xff0c;精准感知 2. 网络层&#xff1a;互联互通&#xff0c;信息共享 3. 平台层&#xff1a;数据分析&#xff0c;智能决策 4. 应用层&#xff1a;精准施策&#xff0…

构建gitlab远端服务器(check->build->test->deploy)

系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录 系列文章目录前言构建gitlab远端服务器一、步骤一:搭建gitlab的运行服务器【运维】1. 第一步:硬件服务器准备工作(1)选择合适的硬件和操作系统linux(2)安装必…

C语言 | Leetcode C语言题解之第240题搜索二维矩阵II

题目&#xff1a; 题解&#xff1a; bool searchMatrix(int** matrix, int matrixSize, int* matrixColSize, int target){int i 0;int j matrixColSize[0] - 1;while(j > 0 && i < matrixSize){if(target < matrix[i][j])j--;else if(target > matrix[…

用户注册业务逻辑、接口设计和实现、前端逻辑

一、用户注册业务逻辑分析 二、用户注册接口设计和定义 2.1. 设计接口基本思路 对于接口的设计&#xff0c;我们要根据具体的业务逻辑&#xff0c;设计出适合业务逻辑的接口。设计接口的思路&#xff1a; 分析要实现的业务逻辑&#xff1a; 明确在这个业务中涉及到几个相关子…

【GraphRAG】微软 graphrag 效果实测

GraphRAG 本文将基于以下来源&#xff0c;对Microsoft GraphRAG分析优缺点、以及示例实测分析。 1. Source 代码仓库&#xff1a; Welcome to GraphRAGhttps://microsoft.github.io/graphrag/ 微软文章1&#xff08;2024.2.13&#xff09;&#xff1a;GraphRAG: Unlocking…

Web开发:四角线框效果(HTML、CSS、JavaScript)

目录 一、实现效果 二、完整代码 三、页面准备 1、页面结构 2、初始样式 3、现有效果 三、线框实现 1、需求分析 2、线框结构 3、线框大小 4、线框位置 5、线框样式 6、移动线框 7、添加过渡效果 8、使用CSS变量 一、实现效果 如下图所示&#xff0c;当鼠标移动…

微服务设计原则——高性能:锁

文章目录 1.锁的问题2.无锁2.1 串行无锁2.2 无锁数据结构 3.减少锁竞争参考文献 1.锁的问题 高性能系统中使用锁&#xff0c;往往带来的坏处要大于好处。 并发编程中&#xff0c;锁带解决了安全问题&#xff0c;同时也带来了性能问题&#xff0c;因为锁让并发处理变成了串行操…