继续上篇实战BootstrapBlazor树型控件Tree内容,本篇主要讲解整合Freesqlorm快速制作数据库后台维护页面 demo演示的是Sqlite驱动,FreeSql支持多种数据库,MySqlSqlServerPostgreSQLOracleSqliteFirebird达梦神通人大金仓翰高华为GaussDBMsAccess1。Freesql处理树形分类 无限级分类(父子)是一种比较常用的表设计,我们直接设计表中包含parentid字段,然后递归查询,fsql内置了方法让使用透明化。1。1父子导航属性 FreeSql导航属性之中,有针对父子关系的设置方式,如下:publicclassArea{〔Column(IsPrimarytrue)〕publicstringCode{get;set;}publicstringName{get;set;}publicstringParentCode{get;set;}〔Navigate(nameof(ParentCode))〕publicAreaParent{get;set;}〔Navigate(nameof(ParentCode))〕publicListChilds{get;set;}} 定义Parent属性,在表达式中可以这样:fsql。Select()。Where(aa。Parent。Parent。Parent。Name中国)。First(); 定义Childs属性,在表达式中可以这样(子查询):fsql。Select()。Where(aa。Childs。AsSelect()。Any(cc。Name北京))。First(); 定义Childs属性,还可以使用【级联保存】、【贪婪加载】等等操作。fsql。Delete()。Where(11)。ExecuteAffrows();varrepofsql。GetRepository();repo。DbContextOptions。EnableAddOrUpdateNavigateListtrue;repo。DbContextOptions。NoneParametertrue;repo。Insert(newArea{Code100000,Name中国,ChildsnewList(new〔〕{newArea{Code110000,Name北京,ChildsnewList(new〔〕{newArea{Code110100,Name北京市},newArea{Code110101,Name东城区},})}})});1。2ToTreeList 配置好父子属性之后,就可以这样用了:vart1fsql。Select()。ToTreeList();Assert。Single(t1);Assert。Equal(100000,t1〔0〕。Code);Assert。Single(t1〔0〕。Childs);Assert。Equal(110000,t1〔0〕。Childs〔0〕。Code);Assert。Equal(2,t1〔0〕。Childs〔0〕。Childs。Count);Assert。Equal(110100,t1〔0〕。Childs〔0〕。Childs〔0〕。Code);Assert。Equal(110101,t1〔0〕。Childs〔0〕。Childs〔1〕。Code); 查询数据本来是平面的,ToTreeList方法将返回的平面数据在内存中加工为树型List返回。1。3AsTreeCte递归删除 很常见的无限级分类表功能,删除树节点时,把子节点也处理一下。fsql。Select()。Where(aa。Name中国)。AsTreeCte()。ToDelete()。ExecuteAffrows();删除中国下的所有记录 如果软删除:fsql。Select()。Where(aa。Name中国)。AsTreeCte()。ToUpdate()。Set(aa。IsDeleted,true)。ExecuteAffrows();软删除中国下的所有记录1。4AsTreeCte递归查询 若不做数据冗余的无限级分类表设计,递归查询少不了,AsTreeCte正是解决递归查询的封装,方法参数说明: 参数 描述 (可选)pathSelector 路径内容选择,可以设置查询返回:中国北京东城区 (可选)up false(默认):由父级向子级的递归查询,true:由子级向父级的递归查询 (可选)pathSeparator 设置pathSelector的连接符,默认: (可选)level 设置递归层级 通过测试的数据库:MySql8。0、SqlServer、PostgreSQL、Oracle、Sqlite、Firebird、达梦、人大金仓、南大通用、翰高 姿势一:AsTreeCte()ToTreeListvart2fsql。Select()。Where(aa。Name中国)。AsTreeCte()查询中国下的所有记录。OrderBy(aa。Code)。ToTreeList();非必须,也可以使用ToList(见姿势二)Assert。Single(t2);Assert。Equal(100000,t2〔0〕。Code);Assert。Single(t2〔0〕。Childs);Assert。Equal(110000,t2〔0〕。Childs〔0〕。Code);Assert。Equal(2,t2〔0〕。Childs〔0〕。Childs。Count);Assert。Equal(110100,t2〔0〕。Childs〔0〕。Childs〔0〕。Code);Assert。Equal(110101,t2〔0〕。Childs〔0〕。Childs〔1〕。Code);WITHastreecteas(SELECT0asctelevel,a。Code,a。Name,a。ParentCodeFROMAreaaWHERE(a。Name中国)unionallSELECTwct1。ctelevel1asctelevel,wct2。Code,wct2。Name,wct2。ParentCodeFROMastreectewct1INNERJOINAreawct2ONwct2。ParentCodewct1。Code)SELECTa。Code,a。Name,a。ParentCodeFROMastreecteaORDERBYa。Code 姿势二:AsTreeCte()ToListvart3fsql。Select()。Where(aa。Name中国)。AsTreeCte()。OrderBy(aa。Code)。ToList();Assert。Equal(4,t3。Count);Assert。Equal(100000,t3〔0〕。Code);Assert。Equal(110000,t3〔1〕。Code);Assert。Equal(110100,t3〔2〕。Code);Assert。Equal(110101,t3〔3〕。Code);执行的SQL与姿势一相同 姿势三:AsTreeCte(pathSelector)ToList 设置pathSelector参数后,如何返回隐藏字段?vart4fsql。Select()。Where(aa。Name中国)。AsTreeCte(aa。Name〔a。Code〕)。OrderBy(aa。Code)。ToList(anew{itema,levelConvert。ToInt32(a。ctelevel),patha。ctepath});Assert。Equal(4,t4。Count);Assert。Equal(100000,t4〔0〕。item。Code);Assert。Equal(110000,t4〔1〕。item。Code);Assert。Equal(110100,t4〔2〕。item。Code);Assert。Equal(110101,t4〔3〕。item。Code);Assert。Equal(中国〔100000〕,t4〔0〕。path);Assert。Equal(中国〔100000〕北京〔110000〕,t4〔1〕。path);Assert。Equal(中国〔100000〕北京〔110000〕北京市〔110100〕,t4〔2〕。path);Assert。Equal(中国〔100000〕北京〔110000〕东城区〔110101〕,t4〔3〕。path);WITHastreecteas(SELECT0asctelevel,a。Name〔a。Code〕asctepath,a。Code,a。Name,a。ParentCodeFROMAreaaWHERE(a。Name中国)unionallSELECTwct1。ctelevel1asctelevel,wct1。ctepathwct2。Name〔wct2。Code〕asctepath,wct2。Code,wct2。Name,wct2。ParentCodeFROMastreectewct1INNERJOINAreawct2ONwct2。ParentCodewct1。Code)SELECTa。Codeas1,a。Nameas2,a。ParentCodeas5,a。ctelevelas6,a。ctepathas7FROMastreecteaORDERBYa。Code 更多姿势。。。请根据代码注释进行尝试https:github。comdotnetcoreFreeSqlwikiE69FA5E8AFA2E788B6E5AD90E585B3E7B3BB2。数据实体类 新建文件ModelTreeDataFsqlFoo。cs 其中GetTreeList()为获取常规List形式,GetTreeItems()是转换为BootstrapBlazor使用的TreeItem格式。usingBootstrapBlazor。Components;usingFreeSql。DataAnnotations;namespaceb05tree;summary无限级分类(父子)是一种比较常用的表设计,表设计中只有parentid字段summarypublicclassTreeDataFsqlFoo{https:github。comdotnetcoreFreeSqlwikiE69FA5E8AFA2E788B6E5AD90E585B3E7B3BB〔Column(IsPrimarytrue,StringLength6)〕publicstringCode{get;set;}〔Column(StringLength20,IsNullablefalse)〕publicstringName{get;set;}〔Column(StringLength6)〕publicstringParentCode{get;set;}〔Navigate(nameof(ParentCode))〕publicTreeDataFsqlFooParent{get;set;}〔Navigate(nameof(ParentCode))〕publicListTreeDataFsqlFooChilds{get;set;}publicstaticvoidDemoDatas(IFreeSqlfsql){varresfsql!。SelectTreeDataFsqlFoo()。Count();if(res0){vardistrictsnewTreeDataFsqlFoo{Code001,Name001系统管理,ChildsnewListTreeDataFsqlFoo(new〔〕{newTreeDataFsqlFoo{Code00101,Name00101基础数据管理,ChildsnewListTreeDataFsqlFoo(new〔〕{newTreeDataFsqlFoo{Code0010101,Name0010101教师},newTreeDataFsqlFoo{Code0010102,Name0010102职工}})},newTreeDataFsqlFoo{Code00102,Name00102餐厅数据管理,ChildsnewListTreeDataFsqlFoo(new〔〕{newTreeDataFsqlFoo{Code0010201,Name0010201厨师},newTreeDataFsqlFoo{Code0010202,Name0010202服务员}})}})};varlazyItemsnewListTreeDataFsqlFoo(new〔〕{districts,newTreeDataFsqlFoo{Code00103,Name00103懒加载演示},newTreeDataFsqlFoo{Code00104,Name00104懒加载延时演示}});varrepofsql。GetRepositoryTreeDataFsqlFoo();仓库类repo。DbContextOptions。EnableAddOrUpdateNavigateListtrue;开启一对多,多对多级联保存功能repo。Insert(lazyItems);}}publicstaticListTreeDataFsqlFooGetTreeList(IFreeSqlfsql){DemoDatas(fsql);returnfsql。SelectTreeDataFsqlFoo()。LeftJoin(dd。ParentCoded。Parent。Code)。ToList();}publicstaticListTreeItemGetTreeItems(IFreeSqlfsql){DemoDatas(fsql);varitemsfsql。SelectTreeDataFsqlFoo()。LeftJoin(dd。ParentCoded。Parent。Code)。ToList(anewTreeItem(){Texta。Name,Ida。Code,ParentIda。ParentCode});算法获取属性结构数据returnitems。CascadingTree()。ToList();}}3。Razor页面 新建文件PagesTreeFsql。razor 上半部分是Tree控件,下半部分是BootstrapBlazor的Table组件,用于维护后台数据pagetreefsqlusingb05tree。Datah3Tree树形控件Freesqlh3p通过设置节点codeHasChildNode控制是否显示节点小箭头图片。通过Tree的OnExpandNode委托添加节点code4。后置代码C 新建文件PagesTreeFsql。razor。csusingBootstrapBlazor。Components;usingMicrosoft。AspNetCore。Components;namespaceb05tree。Pages;summarysummarypublicsealedpartialclassTreeFsql{〔Inject〕IFreeSql?fsql{get;set;}privateListTreeItemGetLazyItems(){varretTreeDataFsqlFoo。GetTreeItems(fsql);ret〔1〕。Text懒加载;ret〔1〕。HasChildNodetrue;ret〔2〕。Text懒加载延时;ret〔2〕。HasChildNodetrue;ret〔2〕。KeyDelay;returnret;}privateTaskOnTreeItemClick(TreeItemitem){Trace。Log(34;TreeItem:{item。Text}clicked);returnTask。CompletedTask;}privateTaskOnTreeItemChecked(TreeItemitem){varstateitem。Checked?选中:未选中;TraceChecked。Log(34;TreeItem:{item。Text}{state});returnTask。CompletedTask;}privatestaticasyncTaskOnExpandNode(TreeItemitem){if(!item。Items。Any()item。HasChildNode!item。ShowLoading){item。ShowLoadingtrue;if(item。Key?。ToString()Delay){awaitTask。Delay(800);}item。Items。AddRange(newTreeItem〔〕{newTreeItem(){Text懒加载子节点1,HasChildNodetrue},newTreeItem(){Text懒加载延时子节点2,HasChildNodetrue,KeyDelay},newTreeItem(){Text懒加载子节点3}});item。ShowLoadingfalse;}}privateTaskOnTreeItemChecked(ListTreeItemitems){TraceCheckedItems。Log(34;当前共选中{items。Count}项);returnTask。CompletedTask;}}5。添加Menu链接 添加代码到SharedNavMenu。razorNavLinkclassnavlinkhreftreefsqlspanclassoioiplusariahiddentruespanTreeFsqlNavLink6。运行调试 更改和删除后,刷新页面马上可以看到效果。 项目源码 Blazor100:Blazor入门100天 知识共享许可协议 本作品采用知识共享署名非商业性使用相同方式共享4。0国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名AlexChow,不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。AlexChow 今日头条博客园知乎GiteeGitHub