当前位置: 首页 > news >正文

分享 .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://www.lryc.cn/news/401898.html

相关文章:

  • 【柴油机故障诊断】基于斑马优化算法ZOA优化柴油机故障诊断附Matlab代码
  • C1W4.Assignment.Naive Machine Translation and LSH
  • 智能听诊器:宠物健康监测的革新者
  • 001、Mac系统上Stable Diffusion WebUI环境搭建
  • k8s一些名词解释
  • ArkUI组件——循环控制/List
  • 定制开发AI智能名片商城微信小程序在私域流量池构建中的应用与策略
  • 网络安全(含面试题版)
  • 牛客 7.13 月赛(留 C逆元 Ddp)
  • LeetCode 92. 反转链表 II
  • mac M1 创建Mysql8.0容器
  • 【Vue3】4个比较重要的设计模式!!
  • Ubuntu安装virtualbox(win10)
  • 二次开发源码 借贷系统uniapp/借贷认证系统/小额信贷系统/工薪贷APP/资金贷系统h5
  • LG 选择 Flutter 来增强其智能电视操作系统 webOS
  • [ACM独立出版] 2024年虚拟现实、图像和信号处理国际学术会议(VRISP 2024,8月2日-4)
  • ASP.NET Core中创建中间件的几种方式
  • Atcoder ABC351 A-E 题解
  • 【终极指南】从零开始征服机器学习:初学者的黄金路线图
  • MongoDB自学笔记(三)
  • 编程中的智慧之设计模式三
  • 《YOLOv10改进实战专栏》专栏介绍 专栏目录
  • Python酷库之旅-第三方库Pandas(030)
  • 神经网络中的激活函数举例,它们各自的特点,以及哪个激活函数效果更好,为什么
  • 【树莓派3B+】控制引脚输出高低电平
  • 【Redis7】高阶篇
  • 在学习使用LabVIEW的过程中,需要注意哪些问题?
  • 网络编程-TCP/IP
  • php在服务器上部署可视化运维工具详细列表
  • Linux中的文件夹作用