close
The Wayback Machine - https://web.archive.org/web/20200917001226/https://github.com/maliming/Abp.GeneralTree
Skip to content
master
Go to file
Code

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
 
 
 
 
 
 

README.md

Image

Abp GeneralTree

Image NuGet

GeneralTree中文文档

  • Based on Abp module system, perfect integration Abp framework.
  • Support for custom primary key (value type, reference type).
  • Automating the assignment of Code,Level,FullName extends other attributes of the entity.
  • Efficient management of entities based on Code, Level features.
  • Suitable for managing a variety of tree structure entities, such as: region, organization, category, industry and other entities with parent-child Entity.

Installation

Install-Package Abp.GeneralTree
dotnet add package Abp.GeneralTree

First you need to add the dependency to your module:

[DependsOn(typeof(GeneralTreeModule))]
public class YourProjectModule : AbpModule
{
    //...
}

GeneralTree provides a generic IGeneralTree interface, which inherits this interface, passing in generic parameter entities and primary keys (primary keys can be value types and reference types)

Value type

public interface IGeneralTree<TTree, TPrimaryKey> : IEntity<TPrimaryKey>
    where TPrimaryKey : struct
{
      string Name { get; set; }

      string FullName { get; set; }

      string Code { get; set; }

      int Level { get; set; }

      TTree Parent { get; set; }

      TPrimaryKey? ParentId { get; set; }

      ICollection<TTree> Children { get; set; }
}

Reference type

public interface IGeneralTreeWithReferenceType<TTree, TPrimaryKey> : IEntity<TPrimaryKey>
    where TPrimaryKey : class
{
      string Name { get; set; }

      string FullName { get; set; }

      string Code { get; set; }

      int Level { get; set; }

      TTree Parent { get; set; }

      TPrimaryKey ParentId { get; set; }

      ICollection<TTree> Children { get; set; }
}

Take the Region entity as an example:

public class Region : Entity<long>, IGeneralTree<Region, long>
{
      public virtual string Name { get; set; }

      public virtual string FullName { get; set; }

      public virtual string Code { get; set; }

      public virtual int Level { get; set; }

      public virtual Region Parent { get; set; }

      public virtual long? ParentId { get; set; }

      public virtual ICollection<Region> Children { get; set; }
}

Entities implement properties under generic interfaces, and GeneralTree automatically maintains these properties (FullName, Code, Level, ParentId...)

To create, update, move, delete, etc., use IGeneralTreeManager<TTree, TPrimaryKey>, and the generic parameters of the interface are the same as above.

Use

We first initialize some regional information.

var beijing = new Region
{
      Name = "beijing"
};
await _generalRegionTreeManager.CreateAsync(beijing);

At this time, the entity information of beijing is as follows:

Id Name FullName Code Level ParentId
1 beijing beijing 00001 1 NULL

GeneralTree automatically maintains the modified properties. It provides the basis for efficient management later.

Add some areas again.

var beijing = new Region
{
      Name = "beijing"
};
await _generalRegionTreeManager.CreateAsync(beijing);
await CurrentUnitOfWork.SaveChangesAsync();

var dongcheng = new Region
{
      Name = "dongcheng",
      ParentId = beijing.Id
};

var xicheng = new Region
{
      Name = "xicheng",
      ParentId = beijing.Id
};
await _generalRegionTreeManager.CreateAsync(dongcheng);
await _generalRegionTreeManager.CreateAsync(xicheng);

var hebei = new Region
{
      Name = "hebei"
};
await _generalRegionTreeManager.CreateAsync(hebei);
await CurrentUnitOfWork.SaveChangesAsync();

var shijianzhuang = new Region
{
      Name = "shijianzhuang",
      ParentId = hebei.Id
};
await _generalRegionTreeManager.CreateAsync(shijianzhuang);
await CurrentUnitOfWork.SaveChangesAsync();

var changanqu = new Region
{
      Name = "changanqu",
      ParentId = shijianzhuang.Id
};
var qiaoxiqu = new Region
{
      Name = "qiaoxiqu",
      ParentId = shijianzhuang.Id
};
await _generalRegionTreeManager.CreateAsync(changanqu);
await _generalRegionTreeManager.CreateAsync(qiaoxiqu);

The results are as follows:

Id Name FullName Code Level ParentId
1 beijing beijing 00001 1 NULL
2 dongcheng beijing-dongcheng 00001.00001 2 1
3 xicheng beijing-xicheng 00001.00002 2 1
4 hebei hebei 00002 1 NULL
5 shijianzhuang hebei-shijianzhuang 00002.00001 2 4
6 changanqu hebei-shijianzhuang-changanqu 00002.00001.00001 3 5
7 qiaoxiqu hebei-shijianzhuang-qiaoxiqu 00002.00001.00002 3 5

The above operation has a batch method BulkCreateAsync

var beijing = new Region
{
      Name = "beijing",
      Children = new List<Region>
      {
            new Region
            {
                  Name = "dongcheng"
            },
            new Region
            {
                  Name = "dongcheng"
            }
      }
};
await _generalRegionTreeManager.BulkCreateAsync(beijing);
await CurrentUnitOfWork.SaveChangesAsync();

var hebei = new Region
{
      Name = "hebei",
      Children = new List<Region>
      {
            new Region
            {
                  Name = "shijiazhuang",
                  Children = new List<Region>
                  {
                        new Region
                        {
                              Name = "changanqu"
                        },
                        new Region
                        {
                              Name = "qiaodongqu"
                        }
                  }
            }
      }
};
await _generalRegionTreeManager.BulkCreateAsync(hebei);
await CurrentUnitOfWork.SaveChangesAsync();

Some operations of the tree entity

// Query all areas below Beijing does not include Beijing)
var beijing = await _regionRepository.FirstOrDefaultAsync(x => x.Name == "beijing");
var beijingChildren = _regionRepository.GetAll().Where(x => x.Id != beijing.Id && x.Code.StartsWith(beijing.Code));

// Query the area below Beijing (all districts)
var beijing = await _regionRepository.FirstOrDefaultAsync(x => x.Name == "beijing");
var beijingChildren = _regionRepository.GetAll().Where(x => x.Level == beijing.Level - 1 && x.Code.StartsWith(beijing.Code));

// Query Changan and all the parent above
var changanqu = await _regionRepository.FirstOrDefaultAsync(x => x.Name == "changanqu");
var parents = await _regionRepository.GetAllListAsync(x => changanqu.Code.StartsWith(x.Code));

// Query Changan top parent.
var changanqu = await _regionRepository.FirstOrDefaultAsync(x => x.Name == "changanqu");
var hebei =  await _regionRepository.FirstOrDefaultAsync(x => x.Level == 1 && changanqu.Code.Contains(x.Code));

Other

public interface IGeneralTreeManager<TTree, TPrimaryKey>
      where TPrimaryKey : struct
      where TTree : class, IGeneralTree<TTree, TPrimaryKey>
{
      Task CreateAsync(TTree tree);

      Task BulkCreateAsync(TTree tree, Action<TTree> childrenAction = null);

      Task CreateChildrenAsync(TTree parent, ICollection<TTree> children, Action<TTree> childrenAction = null);

      Task FillUpAsync(TTree tree, Action<TTree> childrenAction = null);

      Task UpdateAsync(TTree tree, Action<TTree> childrenAction = null);

      Task MoveAsync(TPrimaryKey id, TPrimaryKey? parentId, Action<TTree> childrenAction = null);

      Task DeleteAsync(TPrimaryKey id);
}

Custom

public override void PreInitialize()
{
      // Custom error message
      Configuration.Modules.GeneralTree<Region, long>().ExceptionMessageFactory = tree => $"{tree.Name} already exists!.";

      // Custom node with the same name additional judgment logic
      Configuration.Modules.GeneralTree<Region, long>().CheckSameNameExpression = (regionThis, regionCheck) => regionThis.SomeForeignKey == regionCheck.SomeForeignKey

      // Custom FullName separator
      Configuration.Modules.GeneralTree<Region, long>().Hyphen = "=>";

}

The above code is for the entity's primary key as the value type. If it is a reference type, please use IGeneralTreeWithReferenceType and IGeneralTreeManagerWithReferenceType

Configure GeneralTreeCodeGenerateCode length (default is 5 digits)

[Fact]
public void Test_CreateCode_With_Length()
{
      var generate = new GeneralTreeCodeGenerate(new GeneralTreeCodeGenerateConfiguration()
      {
            CodeLength = 3
      });

      generate.CreateCode().ShouldBe(null);
      generate.CreateCode(42).ShouldBe("042");
      generate.CreateCode(1, 2).ShouldBe("001.002");
      generate.CreateCode(1, 2, 3).ShouldBe("001.002.003");
}

GeneralTreeExtensions ToTree converts the Tree collection to TreeDto (has a hierarchical relationship, sortable)

[Fact]
public void ToTreeOrderBy_Test()
{
      var regions = new List<Regin>
      {
            new Regin
            {
                  Id = 1,
                  Name = "beijing"
            },
            new Regin
            {
                  Id = 2,
                  Name = "bdongcheng",
                  ParentId = 1
            },
            new Regin
            {
                  Id = 3,
                  Name = "axicheng",
                  ParentId = 1
            },
            new Regin
            {
                  Id = 4,
                  Name = "aHebei"
            },
            new Regin
            {
                  Id = 5,
                  Name = "bShijianzhuang",
                  ParentId = 4
            },
            new Regin
            {
                  Id = 6,
                  Name = "aChengde",
                  ParentId = 4
            },
            new Regin
            {
                  Id = 7,
                  Name = "bShuangqiao",
                  ParentId = 6
            },
            new Regin
            {
                  Id = 8,
                  Name = "aShuangluan",
                  ParentId = 6
            }
      };

      var tree = regions.ToTreeOrderBy<Regin, long, string>(x => x.Name).ToList();

      tree.First().Name.ShouldBe("aHebei");
      tree.First().Children.First().Name.ShouldBe("aChengde");
      tree.First().Children.First().Children.First().Name.ShouldBe("aShuangluan");
}
You can’t perform that action at this time.