{"id":16634516,"url":"https://github.com/maliming/abp.generaltree","last_synced_at":"2025-03-16T22:31:01.911Z","repository":{"id":45782237,"uuid":"83876806","full_name":"maliming/Abp.GeneralTree","owner":"maliming","description":"For Abp vNext","archived":false,"fork":false,"pushed_at":"2019-12-23T00:35:59.000Z","size":218,"stargazers_count":156,"open_issues_count":3,"forks_count":32,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-02-27T14:48:12.803Z","etag":null,"topics":["abp","entity","generaltree","tree","tree-structure"],"latest_commit_sha":null,"homepage":"https://github.com/maliming/Owl.GeneralTree","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/maliming.png","metadata":{"files":{"readme":"README.CN.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-03-04T07:51:36.000Z","updated_at":"2024-08-22T11:52:01.000Z","dependencies_parsed_at":"2022-08-26T11:32:15.222Z","dependency_job_id":null,"html_url":"https://github.com/maliming/Abp.GeneralTree","commit_stats":null,"previous_names":[],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maliming%2FAbp.GeneralTree","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maliming%2FAbp.GeneralTree/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maliming%2FAbp.GeneralTree/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maliming%2FAbp.GeneralTree/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maliming","download_url":"https://codeload.github.com/maliming/Abp.GeneralTree/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243830952,"owners_count":20354854,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["abp","entity","generaltree","tree","tree-structure"],"created_at":"2024-10-12T05:39:07.781Z","updated_at":"2025-03-16T22:31:01.628Z","avatar_url":"https://github.com/maliming.png","language":"C#","readme":"\n \u003cimg src=\"https://raw.githubusercontent.com/maliming/Abp.GeneralTree/master/GeneralTree.png\" width=\"200\" height=\"200\" /\u003e \n\n# Abp GeneralTree\n\n[![Build status](https://ci.appveyor.com/api/projects/status/5fvpyg756aushkv7?svg=true)](https://ci.appveyor.com/project/maliming/Abp-Generaltree)\n[![NuGet](https://img.shields.io/nuget/vpre/abp.GeneralTree.svg)](https://www.nuget.org/packages/Abp.GeneralTree)\n\n- 基于Abp模块系统,完美集成Abp框架.\n- 支持自定义主键(值类型，引用类型).\n- 自动分配Code,Level,FullName扩展了实体的其他属性.\n- 基于Code,Level对实体进行有效管理.\n- **适用于管理各种树结构实体，例如：区域，组织，类别，行业和具有父子实体的其他实体.**\n\n## 安装\n\n``` c#\nInstall-Package Abp.GeneralTree\ndotnet add package Abp.GeneralTree\n```\n\n在模块中加入`GeneralTreeModule`依赖\n``` c#\n[DependsOn(typeof(GeneralTreeModule))]\npublic class YourProjectModule : AbpModule\n{\n    //...\n}\n```\n\nGeneralTree提供一个泛型`IGeneralTree`接口,实体继承此接口,传入泛型参数实体和主键(主键可以是值类型和引用类型)\n\n值类型\n\n``` c#\npublic interface IGeneralTree\u003cTTree, TPrimaryKey\u003e : IEntity\u003cTPrimaryKey\u003e\n    where TPrimaryKey : struct\n{\n      string Name { get; set; }\n\n      string FullName { get; set; }\n\n      string Code { get; set; }\n\n      int Level { get; set; }\n\n      TTree Parent { get; set; }\n\n      TPrimaryKey? ParentId { get; set; }\n\n      ICollection\u003cTTree\u003e Children { get; set; }\n}\n```\n\n引用类型\n\n``` c#\npublic interface IGeneralTreeWithReferenceType\u003cTTree, TPrimaryKey\u003e : IEntity\u003cTPrimaryKey\u003e\n    where TPrimaryKey : class\n{\n      string Name { get; set; }\n\n      string FullName { get; set; }\n\n      string Code { get; set; }\n\n      int Level { get; set; }\n\n      TTree Parent { get; set; }\n\n      TPrimaryKey ParentId { get; set; }\n\n      ICollection\u003cTTree\u003e Children { get; set; }\n}\n```\n\n以Region地区实体为例:\n\n``` c#\npublic class Region : Entity\u003clong\u003e, IGeneralTree\u003cRegion, long\u003e\n{\n      public virtual string Name { get; set; }\n\n      public virtual string FullName { get; set; }\n\n      public virtual string Code { get; set; }\n\n      public virtual int Level { get; set; }\n\n      public virtual Region Parent { get; set; }\n\n      public virtual long? ParentId { get; set; }\n\n      public virtual ICollection\u003cRegion\u003e Children { get; set; }\n}\n```\n\n实体实现泛型接口下的属性,GeneralTree会自动维护这些属性(FullName,Code,Level,ParentId...)\n\n创建,更新,移动,删除等操作请使用`IGeneralTreeManager\u003cTTree, TPrimaryKey\u003e`, 接口的泛型参数同上.\n\n## 使用\n\n我们首先初始化一些地区信息.[自动获取中华人民共和国商务部的最新中华人民共和国行政区划代码数据](https://github.com/maliming/RegionTree)\n\n``` c#\nvar beijing = new Region\n{\n      Name = \"北京\"\n};\nawait _generalRegionTreeManager.CreateAsync(beijing);\n```\n\n此时beijing的实体信息如下:\n\nId|Name|FullName|Code|Level|ParentId\n:--:|:--:|:--:|:--:|:--:|:--:\n1|北京|北京|00001|1|NULL\n\nGeneralTree自动维护了修改属性.对后面的高效管理提供基础.\n\n再补充一些地区\n\n``` c#\nvar beijing = new Region\n{\n      Name = \"北京\"\n};\nawait _generalRegionTreeManager.CreateAsync(beijing);\nawait CurrentUnitOfWork.SaveChangesAsync();\n\nvar dongcheng = new Region\n{\n      Name = \"东城区\",\n      ParentId = beijing.Id\n};\n\nvar xicheng = new Region\n{\n      Name = \"西城区\",\n      ParentId = beijing.Id\n};\nawait _generalRegionTreeManager.CreateAsync(dongcheng);\nawait _generalRegionTreeManager.CreateAsync(xicheng);\n\nvar hebei = new Region\n{\n      Name = \"河北\"\n};\nawait _generalRegionTreeManager.CreateAsync(hebei);\nawait CurrentUnitOfWork.SaveChangesAsync();\n\nvar shijianzhuang = new Region\n{\n      Name = \"石家庄\",\n      ParentId = hebei.Id\n};\nawait _generalRegionTreeManager.CreateAsync(shijianzhuang);\nawait CurrentUnitOfWork.SaveChangesAsync();\n\nvar changanqu = new Region\n{\n      Name = \"长安区\",\n      ParentId = shijianzhuang.Id\n};\nvar qiaoxiqu = new Region\n{\n      Name = \"桥东区\",\n      ParentId = shijianzhuang.Id\n};\nawait _generalRegionTreeManager.CreateAsync(changanqu);\nawait _generalRegionTreeManager.CreateAsync(qiaoxiqu);\n```\n\n结果如下:\n\nId|Name|FullName|Code|Level|ParentId\n:--:|:--:|:--:|:--:|:--:|:--:\n1|北京|北京|00001|1|NULL\n2|东城区|北京-东城区|00001.00001|2|1\n3|西城区|北京-西城区|00001.00002|2|1\n4|河北|河北|00002|1|NULL\n5|石家庄|河北-石家庄|00002.00001|2|4\n6|长安区|河北-石家庄-长安区|00002.00001.00001|3|5\n7|桥东区|河北-石家庄-桥东区|00002.00001.00002|3|5\n\n上面的操作有批量的方法`BulkCreateAsync`\n\n```c#\nvar beijing = new Region\n{\n      Name = \"北京\",\n      Children = new List\u003cRegion\u003e\n      {\n            new Region\n            {\n                  Name = \"东城区\"\n            },\n            new Region\n            {\n                  Name = \"东城区\"\n            }\n      }\n};\nawait _generalRegionTreeManager.BulkCreateAsync(beijing);\nawait CurrentUnitOfWork.SaveChangesAsync();\n\nvar hebei = new Region\n{\n      Name = \"河北\",\n      Children = new List\u003cRegion\u003e\n      {\n            new Region\n            {\n                  Name = \"石家庄\",\n                  Children = new List\u003cRegion\u003e\n                  {\n                        new Region\n                        {\n                              Name = \"长安区\"\n                        },\n                        new Region\n                        {\n                              Name = \"桥东区\"\n                        }\n                  }\n            }\n      }\n};\nawait _generalRegionTreeManager.BulkCreateAsync(hebei);\nawait CurrentUnitOfWork.SaveChangesAsync();\n```\n\n## 树形实体的一些操作\n\n```csharp\n// 查询北京下面的所有地区(不包括北京)\nvar beijing = await _regionRepository.FirstOrDefaultAsync(x =\u003e x.Name == \"北京\");\nvar beijingChildren = _regionRepository.GetAll().Where(x =\u003e x.Id != beijing.Id \u0026\u0026 x.Code.StartsWith(beijing.Code));\n\n// 查询北京下面的地区(所有的区)\nvar beijing = await _regionRepository.FirstOrDefaultAsync(x =\u003e x.Name == \"北京\");\nvar beijingChildren = _regionRepository.GetAll().Where(x =\u003e x.Level == beijing.Level - 1 \u0026\u0026 x.Code.StartsWith(beijing.Code));\n\n// 查询长安区和上面所有的父地区\nvar changanqu = await _regionRepository.FirstOrDefaultAsync(x =\u003e x.Name == \"长安区\");\nvar parents = await _regionRepository.GetAllListAsync(x =\u003e changanqu.Code.StartsWith(x.Code));\n\n// 查询长安区的最顶级父地区\nvar changanqu = await _regionRepository.FirstOrDefaultAsync(x =\u003e x.Name == \"长安区\");\nvar hebei =  await _regionRepository.FirstOrDefaultAsync(x =\u003e x.Level == 1 \u0026\u0026 changanqu.Code.Contains(x.Code));\n```\n\n## 其它操作\n\n```c#\npublic interface IGeneralTreeManager\u003cTTree, TPrimaryKey\u003e\n      where TPrimaryKey : struct\n      where TTree : class, IGeneralTree\u003cTTree, TPrimaryKey\u003e\n{\n      // 创建\n      Task CreateAsync(TTree tree);\n\n      // 批量创建\n      Task BulkCreateAsync(TTree tree, Action\u003cTTree\u003e childrenAction = null);\n\n      // 给parnet增加子节点\n      Task CreateChildrenAsync(TTree parent, ICollection\u003cTTree\u003e children, Action\u003cTTree\u003e childrenAction = null);\n\n      // 填充Code,Level,FullName...等属性\n      Task FillUpAsync(TTree tree, Action\u003cTTree\u003e childrenAction = null);\n\n      // 更新属性(如Name, 当前和所有的子节点FullName都会统一更新)\n      Task UpdateAsync(TTree tree, Action\u003cTTree\u003e childrenAction = null);\n\n      // 移动节点的父节点(相关的子节点都会更新Code,Level,FullName...等属性)\n      Task MoveAsync(TPrimaryKey id, TPrimaryKey? parentId, Action\u003cTTree\u003e childrenAction = null);\n\n      // 删除节点\n      Task DeleteAsync(TPrimaryKey id);\n}\n```\n\n## 自定义配置\n\n``` c#\npublic override void PreInitialize()\n{\n      // 自定义错误信息\n      Configuration.Modules.GeneralTree\u003cRegion, long\u003e().ExceptionMessageFactory = tree =\u003e $\"{tree.Name} already exists!.\";\n\n      // 自定义同级节点同名额外检查逻辑\n      Configuration.Modules.GeneralTree\u003cRegion, long\u003e().CheckSameNameExpression = (regionThis, regionCheck) =\u003e regionThis.SomeForeignKey == regionCheck.SomeForeignKey\n\n      // 自定义FullName分隔符\n      Configuration.Modules.GeneralTree\u003cRegion, long\u003e().Hyphen = \"=\u003e\";\n\n}\n```\n\n以上代码都是针对实体的主键为值类型.如果是引用类型请使用`IGeneralTreeWithReferenceType`和`IGeneralTreeManagerWithReferenceType`\n\n## 其它\n\n配置`GeneralTreeCodeGenerate`Code长度(默认是5位)\n\n``` c#\n[Fact]\npublic void Test_CreateCode_With_Length()\n{\n      var generate = new GeneralTreeCodeGenerate(new GeneralTreeCodeGenerateConfiguration()\n      {\n            CodeLength = 3\n      });\n\n      generate.CreateCode().ShouldBe(null);\n      generate.CreateCode(42).ShouldBe(\"042\");\n      generate.CreateCode(1, 2).ShouldBe(\"001.002\");\n      generate.CreateCode(1, 2, 3).ShouldBe(\"001.002.003\");\n}\n```\n\n`GeneralTreeExtensions` `ToTree`转换Tree集合到TreeDto(拥有层级关系,可排序)\n\n``` c#\n[Fact]\npublic void ToTreeOrderBy_Test()\n{\n      var regions = new List\u003cRegin\u003e\n      {\n            new Regin\n            {\n                  Id = 1,\n                  Name = \"b北京\"\n            },\n            new Regin\n            {\n                  Id = 2,\n                  Name = \"b东城区\",\n                  ParentId = 1\n            },\n            new Regin\n            {\n                  Id = 3,\n                  Name = \"a西城区\",\n                  ParentId = 1\n            },\n            new Regin\n            {\n                  Id = 4,\n                  Name = \"a河北\"\n            },\n            new Regin\n            {\n                  Id = 5,\n                  Name = \"b石家庄\",\n                  ParentId = 4\n            },\n            new Regin\n            {\n                  Id = 6,\n                  Name = \"a承德\",\n                  ParentId = 4\n            },\n            new Regin\n            {\n                  Id = 7,\n                  Name = \"b双桥区\",\n                  ParentId = 6\n            },\n            new Regin\n            {\n                  Id = 8,\n                  Name = \"a双滦区\",\n                  ParentId = 6\n            }\n      };\n\n      var tree = regions.ToTreeOrderBy\u003cRegin, long, string\u003e(x =\u003e x.Name).ToList();\n\n      tree.First().Name.ShouldBe(\"a河北\");\n      tree.First().Children.First().Name.ShouldBe(\"a承德\");\n      tree.First().Children.First().Children.First().Name.ShouldBe(\"a双滦区\");\n}\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaliming%2Fabp.generaltree","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaliming%2Fabp.generaltree","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaliming%2Fabp.generaltree/lists"}