1、什么是递归?
递归,简单的来说就是按照某种对应的规则,一直遍历循环,直到结束为止。在应用程序中,更直白的来说,就是自己调用自己。在实际应用中,我们会发现,按照常规的思维想要得到树形(无限级)菜单,根本是不可能的事,循环、遍历、赋值?好像都无法实现。
那么这时,借助我们的递归思想和方法,就将这种不可能变成了可能,把这种复杂变得好像就是这么简单?对,这就是递归,一种突破常规思维的思想,明白它、理解它、应用它,一定会受益无穷!
2、设计表
这里需要说明一下,其实整张表最重要的三个字段呢,我已经在注释中标注出来了,即主键、父节点(上级)、名称,其他的字段都是我在应用的时候附加的,作为练习和理解递归的话,这三个字段已足够。
CREATE TABLE [dbo].[Menu](
[Id] [nvarchar](200) NOT NULL,/*主键*/
[Title] [nvarchar](200) NULL,/*名称*/
[ParentId] [nvarchar](200) NULL,/*父节点*/
[Url] [nvarchar](200) NULL,
[Icon] [nvarchar](200) NULL,
[TabId] [int] IDENTITY(1,1) NOT NULL,
[IsOpen] [bit] NOT NULL,
[HaveChild] [bit] NOT NULL,
[SorTime] [datetime] NOT NULL,
CONSTRAINT [PK_MP_Menu] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
3、使用SQL语句递归:
这里创建成存储过程,方便复用。提供四种递归作为参考:
(1)、获取直属父节点
/*获取该节点的直属父节点*/
CREATE procedure [dbo].[GetParent]
@id nvarchar(200)
as
select m2.* from Menu m1,Menu m2 where m1.parentid=m2.id and m1.id=@id
(2)、获取全部父节点
/*获取该节点的所有父节点*/
CREATE procedure [dbo].[GetParentList]
@id nvarchar(200)
as
with tree as(
select m2.* from Menu m1,Menu m2 where m1.parentid=m2.id and m1.id=@id
union all
select m.* from Menu m,tree t where m.id=t.parentid)
select * from tree;
(3)、获取直属子节点
/*获取该节点的直属子节点*/
CREATE procedure [dbo].[GetNode]
@id nvarchar(200)
as
select m.* from Menu m where m.parentid=@id
(4)、获取全部子节点
/*获取该节点的所有子节点*/
CREATE procedure [dbo].[GetNodeList]
@id nvarchar(200)
as
with tree as(
select m.* from Menu m where m.parentid=@id
union all
select m.* from Menu m,tree t where m.parentid=t.id )
select * from tree;
4、在程序代码中体现:
这里最重要的就是明白递归中,自己调用自己的意义了,很直白的说,就好像子节点的子节点还有子节点,那么我们就要调用直到子节点的子节点没有子节点为止,就是这么个道理,理解了就相当easy,不理解就多理解理解,没有别的办法!
/// <summary>
/// 递归树形节点
/// </summary>
/// <returns></returns>
public List<MenuModel> GetTreeList()
{
var queryData = menu.GetEntity(a => a.ParentId.Equals(null));
List<MenuModel> mList = (from r in queryData select new MenuModel()
{
id = r.Id,
title = r.Title,
parentId = r.ParentId,
url = r.Url,
icon = r.Icon,
tabId = r.TabId,
isOpen = r.IsOpen,
haveChild = r.HaveChild,
sorTime = r.SorTime
}).ToList();
foreach (var item in mList)
{
//递归子节点
item.nodes = GetNodesList(item);
}
return mList;
}
/// <summary>
/// 递归子节点
/// </summary>
/// <param name="Id"></param>
/// <returns></returns>
public List<MenuModel> GetNodesList(MenuModel model)
{
var queryData = menu.GetEntity(a => a.ParentId == model.id);
List<MenuModel> mList = (from r in queryData
select new MenuModel()
{
id = r.Id,
title = r.Title,
parentId = r.ParentId,
url = r.Url,
icon = r.Icon,
tabId = r.TabId,
isOpen = r.IsOpen,
haveChild = r.HaveChild,
sorTime = r.SorTime
}).ToList();
foreach (var item in mList)
{
//递归
item.nodes = GetNodesList(item);
}
return mList.OrderBy(a => a.sorTime).ToList();
}
对了,这里补充一下,创建的模型:
public class MenuModel
{
/// <summary>
/// 主键id
/// </summary>
public string id { get; set; }
/// <summary>
/// 名称
/// </summary>
public string title { get; set; }
/// <summary>
/// 父节点
/// </summary>
public string parentId { get; set; }
/// <summary>
/// layui绑定tab标识(自增)
/// </summary>
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public int tabId { get; set; }
/// <summary>
/// 访问地址
/// </summary>
public string url { get; set; }
/// <summary>
/// 小图标
/// </summary>
public string icon { get; set; }
/// <summary>
/// 是否(默认展开)
/// </summary>
public bool isOpen { get; set; }
/// <summary>
/// 是否还有(子节点)标识
/// </summary>
public bool haveChild { get; set; }
/// <summary>
///时间(排序)
/// </summary>
public DateTime sorTime { get; set; }
/// <summary>
/// 是否已分配(角色分配权限时才有意义)
/// </summary>
public bool isChecked { get; set; }
/// <summary>
/// 子节点
/// (注:与表结构不同的属性,必须添加不映射注解)
/// </summary>
[NotMapped]
public List<MenuModel> nodes { get; set; }
}
5、Web层,返回一组Json数据即可:
/// <summary>
/// 获取树形节点
/// </summary>
/// <returns></returns>
//[HttpPost]
public JsonResult GetTreeList()
{
return Json(menu.GetTreeList());
}
返回Json格式如下:
6、效果图: