IczpNet.AbpTrees.Installer
8.2.0.3
dotnet add package IczpNet.AbpTrees.Installer --version 8.2.0.3
NuGet\Install-Package IczpNet.AbpTrees.Installer -Version 8.2.0.3
<PackageReference Include="IczpNet.AbpTrees.Installer" Version="8.2.0.3" />
paket add IczpNet.AbpTrees.Installer --version 8.2.0.3
#r "nuget: IczpNet.AbpTrees.Installer, 8.2.0.3"
// Install IczpNet.AbpTrees.Installer as a Cake Addin #addin nuget:?package=IczpNet.AbpTrees.Installer&version=8.2.0.3 // Install IczpNet.AbpTrees.Installer as a Cake Tool #tool nuget:?package=IczpNet.AbpTrees.Installer&version=8.2.0.3
IczpNet.AbpTrees
An abp module that provides standard tree structure entity implement.
Create project by Abp Cli
abp new IczpNet.AbpTreesDemo -t module --no-ui
Installation
Install the following NuGet packages. (see how)
- IczpNet.AbpTrees.Domain
- IczpNet.AbpTrees.Application
- IczpNet.AbpTrees.Application.Contracts
- IczpNet.AbpTrees.Domain.Shared
Add DependsOn(typeof(AbpTreesXxxModule))
attribute to configure the module dependencies.
IczpNet.AbpTreesDemo.Domain
F:\Dev\abpvnext\Iczp.AbpTrees\Example\src\IczpNet.AbpTreesDemo.Domain\AbpTreesDemoDomainModule.cs
[DependsOn(typeof(AbpTreesDomainModule))]
IczpNet.AbpTreesDemo.Domain.Shared
[DependsOn(typeof(AbpTreesDomainSharedModule))]
IczpNet.AbpTreesDemo.Application.Contracts
[DependsOn(typeof(AbpTreesApplicationContractsModule))]
IczpNet.AbpTreesDemo.Application
[DependsOn(typeof(AbpTreesApplicationModule))]
Internal structure
IczpNet.AbpTrees.Domain
ITreeEntity
using System.Collections.Generic;
using Volo.Abp.Domain.Entities;
namespace IczpNet.AbpTrees
{
public interface ITreeEntity<T, TKey> : ITreeEntity<TKey>
where T : ITreeEntity<TKey>
where TKey : struct
{
T Parent { get; }
IEnumerable<T> Childs { get; }
void SetName(string name);
void SetParent(T parent);
void SetParentId(TKey? parentId);
}
public interface ITreeEntity<TKey> : IEntity<TKey> where TKey : struct
{
string Name { get; }
TKey? ParentId { get; }
string FullPath { get; }
string FullPathName { get; }
int Depth { get; }
double Sorting { get; set; }
string Description { get; set; }
}
}
TreeEntity
using IczpNet.AbpTrees.Statics;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using Volo.Abp.Domain.Entities.Auditing;
namespace IczpNet.AbpTrees
{
public abstract class TreeEntity<T, TKey> : FullAuditedAggregateRoot<TKey>, ITreeEntity<T, TKey>
where T : ITreeEntity<TKey>
where TKey : struct
{
[MaxLength(64)]
[Required(ErrorMessage = "Name Required.")]
public virtual string Name { get; protected set; }
public virtual TKey? ParentId { get; set; }
[MaxLength(1000)]
[Required]
public virtual string FullPath { get; protected set; }
[MaxLength(1000)]
[Required]
public virtual string FullPathName { get; protected set; }
/// <summary>
/// 层级
/// </summary>
[Range(0, 1024)]
public virtual int Depth { get; protected set; }
public virtual double Sorting { get; set; }
[MaxLength(500)]
public virtual string Description { get; set; }
public virtual int GetChildsCount()
{
return Childs.Count();
}
/// <summary>
/// 父级角色
/// </summary>
[ForeignKey(nameof(ParentId))]
public virtual T Parent { get; protected set; }
/// <summary>
/// 子集合
/// </summary>
public virtual IEnumerable<T> Childs { get; protected set; }
protected TreeEntity()
{
}
protected TreeEntity(TKey id, string name, TKey? parentId) : base(id)
{
SetId(id);
SetParentId(parentId);
SetName(name);
SetFullPath(null);
SetFullPathName(null);
}
public virtual void SetParentId(TKey? parentId)
{
ParentId = parentId;
}
protected virtual void SetId(TKey id)
{
Id = id;
}
public virtual void SetName(string name)
{
Name = name;
}
protected virtual void SetFullPath(string parentPath)
{
FullPath = parentPath.IsNullOrEmpty() ? $"{Id}" : $"{parentPath}{AbpTreesConsts.SplitPath}{Id}";
}
protected virtual void SetFullPathName(string parentPathName)
{
FullPathName = parentPathName.IsNullOrEmpty() ? $"{Name}" : $"{parentPathName}{AbpTreesConsts.SplitPath}{Name}";
}
protected virtual void SetDepth(int depth)
{
Depth = depth;
}
public virtual void SetParent(T parent)
{
if (parent == null)
{
SetDepth(0);
SetFullPath(null);
SetFullPathName(null);
}
else
{
Parent = parent;
Assert.If(Parent.Depth >= AbpTreesConsts.MaxDepth, $"超出最大层级:{AbpTreesConsts.MaxDepth}");
SetDepth(Parent.Depth + 1);
SetFullPath(parent.FullPath);
SetFullPathName(parent.FullPathName);
}
}
}
}
ITreeManager
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Domain.Services;
namespace IczpNet.AbpTrees
{
public interface ITreeManager<T, TKey, TTreeInfo, TWithChildsOuput, TwithParentOuput> : ITreeManager<T, TKey, TTreeInfo, TWithChildsOuput>, IDomainService
where T : ITreeEntity<TKey>
where TKey : struct
where TTreeInfo : ITreeInfo<TKey>
where TWithChildsOuput : ITreeWithChildsInfo<TWithChildsOuput>
where TwithParentOuput : ITreeWithParentInfo<TwithParentOuput>
{
Task<TwithParentOuput> GetWithParentAsync(TKey id);
}
public interface ITreeManager<T, TKey, TTreeInfo, TWithChildsOuput> : ITreeManager<T, TKey, TTreeInfo>, IDomainService
where T : ITreeEntity<TKey>
where TKey : struct
where TTreeInfo : ITreeInfo<TKey>
where TWithChildsOuput : ITreeWithChildsInfo<TWithChildsOuput>
{
Task<List<TWithChildsOuput>> GetAllListWithChildsAsync(TKey? parentId, bool isImportAllChilds = false);
Task<List<TWithChildsOuput>> GetRootListAsync(List<TKey> idList);
}
public interface ITreeManager<T, TKey, TTreeOutput> : ITreeManager<T, TKey>, IDomainService
where T : ITreeEntity<TKey>
where TKey : struct
where TTreeOutput : ITreeInfo<TKey>
{
Task<List<TTreeOutput>> GetAllByCacheAsync();
}
public interface ITreeManager<T, TKey> : IDomainService
where T : ITreeEntity<TKey>
where TKey : struct
{
Task RemoveCacheAsync();
/// <summary>
/// 查找当前目录及所有子目录
/// </summary>
/// <param name="treeEntityIdList"></param>
/// <returns></returns>
Task<IQueryable<T>> QueryCurrentAndAllChildsAsync(IEnumerable<TKey> treeEntityIdList);
/// <summary>
/// 查找当前目录及所有子目录
/// </summary>
/// <param name="treeEntityIdList"></param>
/// <returns></returns>
Task<IQueryable<T>> QueryCurrentAndAllChildsAsync(TKey treeEntityIdList);
/// <summary>
/// 查找当前目录及所有子目录
/// </summary>
/// <param name="fullPath"></param>
/// <returns></returns>
Task<IQueryable<T>> QueryCurrentAndAllChildsAsync(string fullPath);
/// <summary>
/// 查找当前目录及所有子目录
/// </summary>
/// <param name="fullPaths"></param>
/// <returns></returns>
Task<IQueryable<T>> QueryCurrentAndAllChildsAsync(IEnumerable<string> fullPaths);
Task<T> FindAsync(TKey id);
Task<T> GetAsync(TKey id);
Task<List<T>> GetManyAsync(IEnumerable<TKey> idList);
//Task<T> CreateAsync(string name, TKey? parentId, long sorting, string description);
Task<T> CreateAsync(T entity);
Task<T> UpdateAsync(T entity);
Task DeleteAsync(TKey id);
/// <summary>
/// 获取子目录
/// </summary>
/// <param name="entityId"></param>
/// <returns></returns>
Task<List<T>> GetChildsAsync(TKey? entityId);
Task RepairDataAsync();
}
}
TreeManager
using IczpNet.AbpTrees.Statics;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Caching;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Domain.Services;
using Volo.Abp.ObjectMapping;
namespace IczpNet.AbpTrees
{
public class TreeManager<T, TKey, TOutput, TWithChildsOuput, TWithParentOuput> : TreeManager<T, TKey, TOutput, TWithChildsOuput>, ITreeManager<T, TKey, TOutput, TWithChildsOuput, TWithParentOuput>
where T : class, ITreeEntity<T, TKey>
where TKey : struct
where TOutput : class, ITreeInfo<TKey>
where TWithChildsOuput : class, ITreeWithChildsInfo<TWithChildsOuput>
where TWithParentOuput : class, ITreeWithParentInfo<TWithParentOuput>
{
public TreeManager(IRepository<T, TKey> repository) : base(repository) { }
public async Task<TWithParentOuput> GetWithParentAsync(TKey id)
{
var entity = await GetAsync(id);
return ObjectMapper.Map<T, TWithParentOuput>(entity);
}
}
public class TreeManager<T, TKey, TOutput, TWithChildsOuput> : TreeManager<T, TKey, TOutput>, ITreeManager<T, TKey, TOutput, TWithChildsOuput>
where T : class, ITreeEntity<T, TKey>
where TKey : struct
where TOutput : class, ITreeInfo<TKey>
where TWithChildsOuput : class, ITreeWithChildsInfo<TWithChildsOuput>
{
public TreeManager(IRepository<T, TKey> repository) : base(repository) { }
public override Task RemoveCacheAsync()
{
return Cache.RemoveAsync(CacheKey);
}
public virtual async Task<List<TWithChildsOuput>> GetAllListWithChildsAsync(TKey? parentId, bool isImportAllChilds = false)
{
var allList = await GetAllByCacheAsync();
return await GetChildsAsync(allList, parentId, isImportAllChilds);
}
private async Task<List<TWithChildsOuput>> GetChildsAsync(List<TOutput> allList, TKey? parentId, bool isImportAllChilds)
{
var list = new List<TWithChildsOuput>();
foreach (var treeInfo in allList.Where(x => x.ParentId.Equals(parentId) ).ToList())
{
var item = ObjectMapper.Map<TOutput, TWithChildsOuput>(treeInfo);
if (isImportAllChilds)
{
item.Childs = await GetChildsAsync(allList, treeInfo.Id, isImportAllChilds);
}
list.Add(item);
}
return list;
}
public virtual async Task<List<TWithChildsOuput>> GetRootListAsync(List<TKey> idList)
{
var rootList = (await Repository.GetQueryableAsync())
.Where(x => x.ParentId == null)
.WhereIf(idList != null && idList.Any(), x => idList.Contains(x.Id))
.ToList();
return ObjectMapper.Map<List<T>, List<TWithChildsOuput>>(rootList);
}
}
public class TreeManager<T, TKey, TOutput> : TreeManager<T, TKey>, ITreeManager<T, TKey, TOutput>
where T : class, ITreeEntity<T, TKey>
where TKey : struct
where TOutput : class, ITreeInfo<TKey>
{
protected IObjectMapper ObjectMapper => LazyServiceProvider.LazyGetRequiredService<IObjectMapper>();
protected IDistributedCache<List<TOutput>> Cache => LazyServiceProvider.LazyGetRequiredService<IDistributedCache<List<TOutput>>>();
public TreeManager(IRepository<T, TKey> repository) : base(repository) { }
public override Task RemoveCacheAsync()
{
return Cache.RemoveAsync(CacheKey);
}
public virtual Task<List<TOutput>> GetAllByCacheAsync()
{
return Cache.GetOrAddAsync(CacheKey, async () =>
{
var list = (await Repository.GetQueryableAsync()).OrderByDescending(x => x.Sorting).ToList();
var result = new List<TOutput>();
foreach (var item in list)
{
result.Add(ObjectMapper.Map<T, TOutput>(item));
}
return await Task.FromResult(result);
});
}
}
public class TreeManager<T, TKey> : DomainService, ITreeManager<T, TKey>
where T : class, ITreeEntity<T, TKey>
where TKey : struct
{
public virtual string CacheKey => typeof(T).FullName;
public IRepository<T, TKey> Repository { get; }
public TreeManager(IRepository<T, TKey> repository)
{
Repository = repository;
}
public virtual async Task<IQueryable<T>> QueryCurrentAndAllChildsAsync(IEnumerable<TKey> departmentIdList)
{
var fullPathsQueryable = (await Repository.GetQueryableAsync())
.Where(x => departmentIdList.Contains(x.Id))
.Select(x => x.FullPath)
;
var fullPathList = await AsyncExecuter.ToListAsync(fullPathsQueryable);
return await QueryCurrentAndAllChildsAsync(fullPathList);
}
public virtual async Task<IQueryable<T>> QueryCurrentAndAllChildsAsync(IEnumerable<string> fullPaths)
{
var entityPredicate = PredicateBuilder.New<T>();
foreach (var fullPath in fullPaths)
{
entityPredicate = entityPredicate.Or(x => x.FullPath.StartsWith(fullPath));
}
var entityIdQuery = (await Repository.GetQueryableAsync())
.Where(entityPredicate)
;
//Logger.LogDebug("entityIdQuery:\r\n" + entityIdQuery.ToQueryString());
//Logger.LogDebug("entityIdQuery:\r\n" + string.Join(",", entityIdQuery.ToList()));
return entityIdQuery;
}
public virtual Task<IQueryable<T>> QueryCurrentAndAllChildsAsync(string fullPath)
{
return QueryCurrentAndAllChildsAsync(new List<string>() { fullPath });
}
public virtual Task<IQueryable<T>> QueryCurrentAndAllChildsAsync(TKey departmentId)
{
return QueryCurrentAndAllChildsAsync(new List<TKey>() { departmentId });
}
public virtual Task RemoveCacheAsync()
{
//return Cache.RemoveAsync(CacheKey);
return Task.CompletedTask;
}
public virtual Task<T> FindAsync(TKey id)
{
return Repository.FindAsync(id);
}
public virtual Task<T> GetAsync(TKey id)
{
return Repository.GetAsync(id);
}
public virtual Task<List<T>> GetManyAsync(IEnumerable<TKey> idList)
{
return Repository.GetListAsync(x => idList.Contains(x.Id));
}
public virtual async Task<T> CreateAsync(T inputEntity)
{
Assert.If(await Repository.CountAsync(x => x.Name == inputEntity.Name) > 0, $"Already exists:{inputEntity.Name}");
if (inputEntity.ParentId.HasValue)
{
var parent = await Repository.GetAsync(inputEntity.ParentId.Value);
Assert.NotNull(parent, $"No such parent entity:{inputEntity.ParentId}");
inputEntity.SetParent(parent);
}
else
{
inputEntity.SetParent(null);
}
var entity = await Repository.InsertAsync(inputEntity, autoSave: true);
await RemoveCacheAsync();
return entity;
}
public virtual async Task<T> UpdateAsync(T entity)
{
Assert.NotNull(entity, $"an entity is no such.");
Assert.NotNull(entity.Name, $"[Name] cannot be null.");
Assert.If(entity.Name.Contains(AbpTreesConsts.SplitPath), $"[Name] cannot contains char:\"/\"");
Assert.If(await Repository.CountAsync(x => x.Name == entity.Name && !x.Id.Equals(entity.Id)) > 0, $" Name[{entity.Name}] already such.");
//entity.SetName(entity.Name);
if (entity.ParentId.HasValue)
{
//变更上级
var parent = await Repository.GetAsync(entity.ParentId.Value);
Assert.NotNull(parent, $"[Parent] is no such.");
entity.SetParent(parent);
}
else
{
entity.SetParent(null);
}
//update childs
await ChangeChildsAsync(entity);
await RemoveCacheAsync();
return entity;
}
protected virtual async Task ChangeChildsAsync(T entiy)
{
Logger.LogInformation($"ChangeChilds id:{entiy.Id}");
foreach (var item in entiy.Childs)
{
item.SetParent(entiy);
await ChangeChildsAsync(item);
}
}
public virtual async Task DeleteAsync(TKey id)
{
var entity = await Repository.GetAsync(id);
var childCount = entity.Childs.Count();
Assert.If(childCount > 0, $"Has ({childCount}) childs, cannot delete.");
await Repository.DeleteAsync(entity);
await RemoveCacheAsync();
}
public async Task<List<T>> GetChildsAsync(TKey? entityId)
{
//return await Repository.GetListAsync(x => x.ParentId == departmentId);
return (await Repository.GetQueryableAsync())
.Where(x => x.ParentId.Equals(entityId))
.OrderByDescending(x => x.Sorting)
.ToList();
}
public virtual async Task RepairDataAsync()
{
var list = await Repository.GetListAsync(x => x.ParentId == null);
foreach (var entity in list)
{
await SetEntityAsync(entity);
await UpdateAsync(entity);
}
}
protected virtual Task SetEntityAsync(T entity)
{
Logger.LogInformation($"SetEntityAsync:{entity}");
entity.SetName(entity.Name);
return Task.CompletedTask;
}
}
}
IczpNet.AbpTrees.Application.Contracts
ITreeAppService
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
namespace IczpNet.AbpTrees
{
public interface ITreeAppService<TGetOutputDto, TGetListOutputDto, TKey, in TGetListInput, in TCreateInput, in TUpdateInput, TTreeInfo>
: ITreeAppService<TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
where TKey : struct
where TTreeInfo : ITreeInfo<TKey>
{
Task<TTreeInfo> GetItemByCacheAsync(TKey id);
Task<List<TTreeInfo>> GetManayByCacheAsync(List<TKey> idList);
Task<List<TTreeInfo>> GetAllByCacheAsync();
}
public interface ITreeAppService<TGetOutputDto, TGetListOutputDto, TKey, in TGetListInput, in TCreateInput, in TUpdateInput>
: ICrudAppService<TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
where TKey : struct
{
Task<List<TGetOutputDto>> GetManyAsync(List<TKey> idList);
Task<DateTime> RepairDataAsync();
}
}
Dtos
ITreeGetListInput
namespace IczpNet.AbpTrees.Dtos
{
public interface ITreeGetListInput<TKey> where TKey : struct
{
bool IsEnabledParentId { get; set; }
int? Depth { get; set; }
TKey? ParentId { get; set; }
string Keyword { get; set; }
}
}
ITreeInput
using System;
namespace IczpNet.AbpTrees.Dtos
{
public interface ITreeInput<TKey> where TKey : struct
{
string Name { get; set; }
TKey? ParentId { get; set; }
}
}
TreeGetListInput
using System.ComponentModel;
using Volo.Abp.Application.Dtos;
namespace IczpNet.AbpTrees.Dtos
{
public class TreeGetListInput<TKey> : PagedAndSortedResultRequestDto, ITreeGetListInput<TKey> where TKey : struct
{
[DefaultValue(false)]
public virtual bool IsEnabledParentId { get; set; }
[DefaultValue(null)]
public virtual int? Depth { get; set; }
[DefaultValue(null)]
public virtual TKey? ParentId { get; set; }
[DefaultValue(null)]
public virtual string Keyword { get; set; }
}
}
IczpNet.AbpTrees.Application
TreeAppService
using IczpNet.AbpTrees.Dtos;
using IczpNet.AbpTrees.Statics;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
namespace IczpNet.AbpTrees
{
public abstract class TreeAppService<TEntity, TKey, TGetOutputDto, TGetListOutputDto, TGetListInput, TCreateInput, TUpdateInput, TTreeInfo> :
TreeAppService<TEntity, TKey, TGetOutputDto, TGetListOutputDto, TGetListInput, TCreateInput, TUpdateInput>,
ITreeAppService<TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput, TTreeInfo>
where TEntity : class, ITreeEntity<TEntity, TKey>
where TKey : struct
where TGetOutputDto : IEntityDto<TKey>
where TGetListOutputDto : IEntityDto<TKey>
where TGetListInput : ITreeGetListInput<TKey>
where TCreateInput : ITreeInput<TKey>
where TUpdateInput : ITreeInput<TKey>
where TTreeInfo : ITreeInfo<TKey>
{
protected ITreeManager<TEntity, TKey, TTreeInfo> TreeCacheManager => LazyServiceProvider.LazyGetRequiredService<ITreeManager<TEntity, TKey, TTreeInfo>>();
protected TreeAppService(IRepository<TEntity, TKey> repository) : base(repository) { }
[HttpGet]
public virtual Task<TTreeInfo> GetItemByCacheAsync(TKey id)
{
return TreeCacheManager.GetItemByCacheAsync(id);
}
[HttpGet]
public virtual Task<List<TTreeInfo>> GetManayByCacheAsync(List<TKey> idList)
{
return TreeCacheManager.GetManyByCacheAsync(idList);
}
[HttpGet]
public virtual async Task<List<TTreeInfo>> GetAllByCacheAsync()
{
await CheckGetListPolicyAsync();
return await TreeCacheManager.GetAllByCacheAsync();
}
}
public abstract class TreeAppService<TEntity, TKey, TGetOutputDto, TGetListOutputDto, TGetListInput, TCreateInput, TUpdateInput> :
CrudAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>,
ITreeAppService<TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
where TEntity : class, ITreeEntity<TEntity, TKey>
where TKey : struct
where TGetOutputDto : IEntityDto<TKey>
where TGetListOutputDto : IEntityDto<TKey>
where TGetListInput : ITreeGetListInput<TKey>
where TCreateInput : ITreeInput<TKey>
where TUpdateInput : ITreeInput<TKey>
{
protected virtual string RepairDataPolicyName { get; set; }
protected virtual ITreeManager<TEntity, TKey> TreeManager => LazyServiceProvider.LazyGetRequiredService<ITreeManager<TEntity, TKey>>();
public TreeAppService(IRepository<TEntity, TKey> repository) : base(repository) { }
protected override IQueryable<TEntity> ApplyDefaultSorting(IQueryable<TEntity> query)
{
return query.OrderByDescending(x => x.Sorting);
}
[HttpGet]
public override Task<TGetOutputDto> GetAsync(TKey id)
{
return base.GetAsync(id);
}
[HttpGet]
public virtual async Task<List<TGetOutputDto>> GetManyAsync(List<TKey> idList)
{
var list = new List<TGetOutputDto>();
foreach (var id in idList)
{
list.Add(await GetAsync(id));
}
return list;
}
[HttpGet]
public override Task<PagedResultDto<TGetListOutputDto>> GetListAsync(TGetListInput input)
{
return base.GetListAsync(input);
}
protected override async Task<IQueryable<TEntity>> CreateFilteredQueryAsync(TGetListInput input)
{
Assert.If(!input.IsEnabledParentId && input.ParentId != null, "When [IsEnabledParentId]=false,then [ParentId] != null");
return (await base.CreateFilteredQueryAsync(input))
.WhereIf(input.DepthList != null && input.DepthList.Any(), x => input.DepthList.Contains(x.Depth))
.WhereIf(input.IsEnabledParentId, x => x.ParentId.Equals(input.ParentId))
//.WhereIf(!string.IsNullOrWhiteSpace(input.Keyword), x => x.Name.Contains(input.Keyword))
;
}
[HttpPost]
public override async Task<TGetOutputDto> CreateAsync(TCreateInput input)
{
await CheckCreatePolicyAsync();
var inputEntity = MapToEntity(input);
inputEntity.SetName(input.Name);
inputEntity.SetParentId(input.ParentId);
var entity = await TreeManager.CreateAsync(inputEntity);
return ObjectMapper.Map<TEntity, TGetOutputDto>(entity);
}
[HttpPost]
public override async Task<TGetOutputDto> UpdateAsync(TKey id, TUpdateInput input)
{
await CheckUpdatePolicyAsync();
var entity = await GetEntityByIdAsync(id);
await MapToEntityAsync(input, entity);
entity.SetName(input.Name);
entity.SetParentId(input.ParentId);
await TreeManager.UpdateAsync(entity);
return await MapToGetOutputDtoAsync(entity);
}
[HttpPost]
public override async Task DeleteAsync(TKey id)
{
await CheckDeletePolicyAsync();
await TreeManager.DeleteAsync(id);
}
[HttpPost]
public virtual async Task<DateTime> RepairDataAsync()
{
await CheckRepairDataPolicyAsync();
await TreeManager.RepairDataAsync();
return Clock.Now;
}
protected virtual async Task CheckRepairDataPolicyAsync()
{
await CheckPolicyAsync(RepairDataPolicyName);
}
}
}
Usage
https://github.com/Iczp/AbpTrees/tree/master/Example
Create a entity
Create a entity [
Department
] and implementTreeEntity<T>
.using IczpNet.AbpTrees; using System; namespace IczpNet.AbpTreesDemo.Departments { public class Department : TreeEntity<Department, Guid> { } }
Create Model
- Create
DepartmentInfo
and implementTreeInfo
in projectIczpNet.AbpTreesDemo.Domain.Shared
using IczpNet.AbpTrees;
using System;
namespace IczpNet.AbpTreesDemo.Departments
{
public class DepartmentInfo : TreeInfo<Guid>
{
}
}
Repository
IczpNet.AbpTreesDemo.EntityFrameworkCore
AbpTreesDemoDbContext.cs
public DbSet<Department> Department { get; }
using IczpNet.AbpTreesDemo.Departments;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
namespace IczpNet.AbpTreesDemo.EntityFrameworkCore;
[ConnectionStringName(AbpTreesDemoDbProperties.ConnectionStringName)]
public class AbpTreesDemoDbContext : AbpDbContext<AbpTreesDemoDbContext>, IAbpTreesDemoDbContext
{
/* Add DbSet for each Aggregate Root here. Example:
* public DbSet<Question> Questions { get; set; }
*/
public AbpTreesDemoDbContext(DbContextOptions<AbpTreesDemoDbContext> options)
: base(options)
{
}
/// <summary>
/// Department
/// </summary>
public DbSet<Department> Department { get; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ConfigureAbpTreesDemo();
}
}
AbpTreesDemoDbContextModelCreatingExtensions.cs
using IczpNet.AbpTreesDemo.Departments;
using Microsoft.EntityFrameworkCore;
using Volo.Abp;
using Volo.Abp.EntityFrameworkCore.Modeling;
namespace IczpNet.AbpTreesDemo.EntityFrameworkCore;
public static class AbpTreesDemoDbContextModelCreatingExtensions
{
public static void ConfigureAbpTreesDemo(
this ModelBuilder builder)
{
Check.NotNull(builder, nameof(builder));
builder.Entity<Department>(b =>
{
//Configure table & schema name
b.ToTable(AbpTreesDemoDbProperties.DbTablePrefix + nameof(Department), AbpTreesDemoDbProperties.DbSchema);
b.ConfigureByConvention();
//Indexes
b.HasIndex(q => q.CreationTime);
});
}
}
Create Dto
IczpNet.AbpTreesDemo.Application.Contracts
DepartmentCreateInput
using IczpNet.AbpTrees.Dtos;
using System;
namespace IczpNet.AbpTreesDemo.Departments.Dtos;
/// <summary>
/// DepartmentCreateInput
/// </summary>
public class DepartmentCreateInput : DepartmentUpdateInput, ITreeInput<Guid>
{
}
DepartmentDto.cs
using System;
using Volo.Abp.Application.Dtos;
namespace IczpNet.AbpTreesDemo.Departments.Dtos
{
public class DepartmentDto : DepartmentInfo, IEntityDto<Guid>
{
public virtual double Sorting { get; set; }
public virtual string Description { get; set; }
}
}
DepartmentGetAllListWithChildsInput.cs
using System;
using System.ComponentModel;
namespace IczpNet.AbpTreesDemo.Departments.Dtos;
public class DepartmentGetAllListWithChildsInput
{
[DefaultValue(null)]
public virtual Guid? ParentId { get; set; }
public virtual bool IsImportAllChilds { get; set; }
}
DepartmentGetListInput.cs
using IczpNet.AbpTrees.Dtos;
using System;
namespace IczpNet.AbpTreesDemo.Departments.Dtos;
public class DepartmentGetListInput : TreeGetListInput<Guid>
{
}
DepartmentUpdateInput.cs
using IczpNet.AbpTrees.Dtos;
using System;
namespace IczpNet.AbpTreesDemo.Departments.Dtos;
public class DepartmentUpdateInput : ITreeInput<Guid>
{
public virtual Guid? ParentId { get; set; }
public virtual string Name { get; set; }
public virtual double Sorting { get; set; }
public virtual string Description { get; set; }
}
DepartmentWithChildsDto.cs
using IczpNet.AbpTrees;
using System;
namespace IczpNet.AbpTreesDemo.Departments.Dtos;
public class DepartmentWithChildsDto : TreeWithChildsInfo<DepartmentWithChildsDto, Guid>
{
public virtual int ChildsCount { get; set; }
}
DepartmentWithParentDto.cs
using IczpNet.AbpTrees;
using System;
namespace IczpNet.AbpTreesDemo.Departments.Dtos;
public class DepartmentWithParentDto : TreeWithParentInfo<DepartmentWithParentDto, Guid>
{
public virtual double Sorting { get; set; }
public virtual string Description { get; set; }
}
interface CRUD
IDepartmentAppSevice and implement ICrudAppService
, ITreeAppService
using IczpNet.AbpTrees;
using IczpNet.AbpTreesDemo.Departments.Dtos;
using System;
namespace IczpNet.AbpTreesDemo.Departments
{
public interface IDepartmentAppSevice :
ITreeAppService<DepartmentDto,
DepartmentDto,
Guid,
DepartmentGetListInput,
DepartmentCreateInput,
DepartmentUpdateInput, DepartmentInfo>
{
}
}
Application CRUD
IczpNet.AbpTreesDemo.Application
> DepartmentAppsevice.cs
using IczpNet.AbpTrees;
using IczpNet.AbpTreesDemo.Departments.Dtos;
using Microsoft.AspNetCore.Mvc;
using System;
using Volo.Abp.Domain.Repositories;
namespace IczpNet.AbpTreesDemo.Departments
{
[Route($"Api/App/{AbpTreesDemoRemoteServiceConsts.ModuleName}/[Controller]/[Action]")]
public class DepartmentAppService
: TreeAppService<
Department,
Guid,
DepartmentDto,
DepartmentDto,
DepartmentGetListInput,
DepartmentCreateInput,
DepartmentUpdateInput,
DepartmentInfo>,
IDepartmentAppSevice
{
public DepartmentAppService(IRepository<Department, Guid> repository) : base(repository)
{
}
}
}
Dto Mapper
AbpTreesDemoApplicationAutoMapperProfile
using AutoMapper;
using IczpNet.AbpTreesDemo.Departments;
using IczpNet.AbpTreesDemo.Departments.Dtos;
using Volo.Abp.AutoMapper;
namespace IczpNet.AbpTreesDemo;
public class AbpTreesDemoApplicationAutoMapperProfile : Profile
{
public AbpTreesDemoApplicationAutoMapperProfile()
{
/* You can configure your AutoMapper mapping configuration here.
* Alternatively, you can split your mapping configurations
* into multiple profile classes for a better organization. */
CreateMap<Department, DepartmentDto>(MemberList.Destination);
CreateMap<Department, DepartmentWithParentDto>(MemberList.Destination);
CreateMap<Department, DepartmentWithChildsDto>(MemberList.Destination)
.ForMember(s => s.ChildsCount, map => map.MapFrom(d => d.GetChildsCount()))
//.ForMember(s => s.UserCount, map => map.MapFrom(d => d.GetUserCount()))
;
CreateMap<DepartmentCreateInput, Department>(MemberList.Source).IgnoreAllPropertiesWithAnInaccessibleSetter();
CreateMap<DepartmentUpdateInput, Department>(MemberList.Source).IgnoreAllPropertiesWithAnInaccessibleSetter();
CreateMap<Department, DepartmentInfo>();
CreateMap<DepartmentInfo, DepartmentWithChildsDto>()
.Ignore(x => x.ChildsCount)
.Ignore(x => x.Childs);
}
}
Add-Migration IczpNet.AbpTreesDemo.HttpApi.Host
Select Project
IczpNet.AbpTreesDemo.HttpApi.Host
, Set Run Start.Open PM
PM> Add-Migration Department_Init
PM> Update-Database
Add Controller
AbpTreesDemoHttpApiHostModule.cs
//... public override void ConfigureServices(ServiceConfigurationContext context) { //... Configure<AbpAspNetCoreMvcOptions>(options => { options .ConventionalControllers .Create(typeof(AbpTreesDemoApplicationModule).Assembly); }); //... } //...
Run
Set as Startup Project:
IczpNet.AbpTreesDemo.HttpApi.Host
ConnectionStrings:
appsettings.json
{ "App": { "CorsOrigins": "https://*.AbpTreesDemo.com,http://localhost:4200,http://localhost:44307,https://localhost:44307" }, "ConnectionStrings": { "Default": "Server=localhost;Initial Catalog=AbpTreesDemo_Main;User ID=sa;Password=123;TrustServerCertificate=True", "AbpTreesDemo": "Server=localhost;Initial Catalog=AbpTreesDemo_Module;User ID=sa;Password=123;TrustServerCertificate=True" }, "Redis": { "Configuration": "127.0.0.1" }, "AuthServer": { "Authority": "https://localhost:44362/", "RequireHttpsMetadata": "false", "SwaggerClientId": "AbpTreesDemo_Swagger", "SwaggerClientSecret": "1q2w3e*" } }
Set PM(Package Management Console) default Project:
IczpNet.AbpTreesDemo.HttpApi.Host
add-migration and update database
PM> Add-Migration Department_Init
PM> Update-Database
Upgrade v1.0.13(Add Property ChildrenCount)
--update ChildrenCount
UPDATE [dbo].[AbpTreesDemoDepartment]
SET [dbo].[AbpTreesDemoDepartment].ChildrenCount = (
SELECT COUNT(1) FROM [dbo].[AbpTreesDemoDepartment] WHERE ParentId=x.Id
)
FROM [dbo].[AbpTreesDemoDepartment] x
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net8.0
- Volo.Abp.VirtualFileSystem (>= 8.2.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on IczpNet.AbpTrees.Installer:
Package | Downloads |
---|---|
IczpNet.Chat.Installer
IczpNet.Chat |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
8.2.0.3 | 93 | 7/18/2024 |
8.2.0.2 | 98 | 7/17/2024 |
8.2.0.1 | 98 | 7/16/2024 |
0.2.4 | 139 | 2/7/2024 |
0.2.3 | 118 | 2/6/2024 |
0.2.2 | 104 | 2/5/2024 |
0.2.1 | 207 | 7/14/2023 |
0.2.0 | 178 | 7/14/2023 |
0.1.21 | 169 | 6/28/2023 |
0.1.20 | 157 | 6/21/2023 |
0.1.19 | 159 | 6/21/2023 |
0.1.18 | 162 | 6/20/2023 |
0.1.17 | 169 | 6/19/2023 |
0.1.16 | 170 | 6/7/2023 |
0.1.15 | 164 | 6/7/2023 |
0.1.14 | 145 | 5/30/2023 |
0.1.13 | 181 | 5/23/2023 |
0.1.12 | 199 | 4/21/2023 |
0.1.11 | 195 | 4/21/2023 |
0.1.10 | 215 | 4/8/2023 |
0.1.9 | 216 | 4/3/2023 |
0.1.8 | 232 | 3/24/2023 |
0.1.7 | 252 | 3/3/2023 |
0.1.6 | 348 | 11/24/2022 |
0.1.5 | 342 | 11/19/2022 |
0.1.4 | 328 | 11/18/2022 |
0.1.3 | 346 | 11/14/2022 |
0.1.2 | 350 | 11/14/2022 |
0.1.1 | 355 | 11/14/2022 |
0.1.0 | 357 | 11/14/2022 |