1.1 Core.Extensions.TreeExtension
namespace Core.Extensions
{
/// <summary>
/// 【树型结构扩展--类】
/// <remarks>
/// 摘要:
/// 以泛型方式定义递归方法把具有父子关系的类中的所有实例,以树型结构进行存储。
/// </remarks>
/// </summary>
public static class TreeExtension
{
///<typeparam name="T">泛型类型实例(1个指定类的类型实例)。</typeparam>
///<param name="all">列表实例,该实例存储着1个指定类的所有实例。</param>
///<param name="currentParentItem">将要添加所有子节点实例的父节点实例。</param>
///<param name="parentId">父节点实例所对应的长整型编号值,默认值:null。</param>
///<param name="idProperty">1个指定类中指定“编号”属性成员的名称,默认值:"Id"。</param>
///<param name="parentIdProperty">1个指定类中指定“父编号”属性成员的名称,默认值:"ParentId"。</param>
///<param name="childrenProperty">1个指定类中指定“子类型集”属性成员的名称,默认值:"ChildrenCollection"。</param>
/// <summary>
/// 【树型数据实例构建】
/// <remarks>
/// 摘要:
/// 该方法通过泛型方式定义递归方法把具有父子关系的类中的所有实例,以树型结构进行存储。
/// </remarks>
/// </summary>
public static void TreeBuilder<T>(List<T> all, T currentParentItem, long? parentId = null,
string idProperty = "Id", string parentIdProperty = "ParentId", string childrenProperty = "ChildrenCollection")
{
List<T> _childrenList = new List<T>();
if (parentId == null || parentId == 0)
{
_childrenList = all.Where(a => a.GetType().GetProperty(parentIdProperty)?.GetValue(a) == (object)parentId).ToList();
}
else
{
_childrenList = all.Where(a => a.GetType().GetProperty(parentIdProperty)?.GetValue(a)?.ToString() == parentId.ToString()).ToList();
}
if(_childrenList.Count > 0)
currentParentItem.GetType().GetProperty(childrenProperty)?.SetValue(currentParentItem, _childrenList);
foreach (var item in _childrenList)
{
long _parentId = Convert.ToInt64(item.GetType().GetProperty(idProperty)?.GetValue(item));
TreeBuilder(all, item, _parentId);
}
}
}
}文章来源:https://www.toymoban.com/news/detail-426453.html
1.2 WebApi.Models. SelectListTreeModel
namespace WebApi.Models
{
/// <summary>
/// 【下拉框控件的树型结构扩展--类】
/// <remarks>
/// 摘要:
/// 以泛型方式定义递归方法把具有父子关系的类中的所有实例,以树型结构进行存储,为下拉框控件以树型结构渲染显示提供数据支撑。
/// </remarks>
/// </summary>
public record SelectListTreeModel
{
/// <summary>
/// 【编号】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定节点实例的长整型编号值。
/// </remarks>
/// </summary>
public long Id { get; set; }
/// <summary>
/// 【上级编号】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定节点实例的上级节点实例的长整型编号值(根级的父编号值为:null/0)。
/// </remarks>
/// </summary>
public long? ParentId { get; set; }
/// <summary>
/// 【名称】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定节点实例所对应的名称。
/// </remarks>
/// </summary>
public string Text { get; set; }
/// <summary>
/// 【最后?】
/// <remarks>
/// 摘要:
/// 获取/设置1个值false(不是最后节点,无下级节点)/true(是最后节点,有下级节点),该值指示个指定节点实例是否是最后节点,有下级节点。
/// </remarks>
/// </summary>
public bool Last { get; set; }
/// <summary>
/// 【选中?】
/// <remarks>
/// 摘要:
/// 获取/设置1个值false(未选中)/true(选中),该值指示个指定节点实例是否是下拉框控件中被选中的节点(用于多选下拉框控件?)。
/// </remarks>
/// </summary>
public string Checked { get; set; } = "0";
/// <summary>
/// 【子节点实例集】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定节点实例所对应的所有子节点实例。
/// </remarks>
/// </summary>
public List<SelectListTreeModel> ChildrenCollection { get; set; }
}
}
1.3 WebApi.Extensions. SelectListTreeExtension
using WebApi.Models;
namespace WebApi.Extensions
{
/// <summary>
/// 【下拉框控件树型结构模型--纪录】
/// </summary>
/// <remarks>
/// 摘要:
/// 通过该纪录的属性成员对下拉框控件以树型结构渲染显示提供数据支撑。
/// </remarks>
public static class SelectListTreeExtension
{
///<typeparam name="T">泛型类型实例(1个指定类的类型实例)。</typeparam>
///<param name="all">列表实例,该实例存储着1个指定类的所有实例。</param>
///<param name="parentId">父节点实例所对应的长整型编号值,默认值:null。</param>
///<param name="idProperty">1个指定类中指定“编号”属性成员的名称,默认值:"Id"。</param>
///<param name="parentIdProperty">1个指定类中指定“父编号”属性成员的名称,默认值:"ParentId"。</param>
///<param name="nameProperty">1个指定类中指定“名称”属性成员的名称,默认值:"Name"。</param>
/// <summary>
/// 【下拉框控件的树型结构】
/// <remarks>
/// 摘要:
/// 该方法通过泛型方式定义递归方法把具有父子关系的类中的所有实例,以树型结构进行存储,为下拉框控件以树型结构渲染显示提供数据支撑。
/// </remarks>
/// </summary>
public static List<SelectListTreeModel> SelectListTree<T>(List<T> all, long? parentId = null,
string idProperty = "Id", string parentIdProperty = "ParentId", string nameProperty = "Name")
{
List<SelectListTreeModel> _selectListTreeModel = new List<SelectListTreeModel>();
List<T> _childrenList = new List<T>();
if (parentId == null || parentId == 0)
{
_childrenList = all.Where(a => a.GetType().GetProperty(parentIdProperty)?.GetValue(a) == (object)parentId).ToList();
}
else
{
_childrenList = all.Where(a => a.GetType().GetProperty(parentIdProperty)?.GetValue(a)?.ToString() == parentId.ToString()).ToList();
}
foreach (var item in _childrenList)
{
var parentTree = new SelectListTreeModel();
parentTree.Id = Convert.ToInt64(item.GetType().GetProperty(idProperty).GetValue(item));
parentTree.ParentId = item.GetType().GetProperty(parentIdProperty)?.GetValue(item) == null ? 0 : Convert.ToInt64(item.GetType().GetProperty(parentIdProperty)?.GetValue(item));
parentTree.Text = item.GetType().GetProperty(nameProperty).GetValue(item).ToString();
parentTree.Last = parentId == null? !all.Exists(a => a.GetType().GetProperty(parentIdProperty)?.GetValue(a) == (object)parentId)
: !all.Exists(a => a.GetType().GetProperty(parentIdProperty)?.GetValue(a)?.ToString() == parentId.ToString());
_selectListTreeModel.Add(parentTree);
parentTree.ChildrenCollection = SelectListTree(all, parentTree.Id);
}
return _selectListTreeModel;
}
}
}
2 准备工作
2.1 Core.Domain. Category
namespace Core.Domain.Catalog
{
/// <summary>
/// 【产品类型--类】
/// <remarks>
/// 摘要:
/// 通过该实体类及其属性成员,用于实现当前程序【Core】.【领域】.【产品类型】.【产品类型】实体与“[ShopDemo].[Category]”表之间的CURD的交互操作,并把这些数据存储到数据库设置实例中(内存)。
/// </remarks>
/// </summary>
public class Category : BaseEntity
{
#region 属性
/// <summary>
/// 【上级产品类型编号】
/// <remarks>
/// 摘要:
/// 获取/设置上级菜单实体1个指定实例上级产品类型的长整型编号值(根级的父编号值为:0)。
/// 注意:
/// 要实现产品类型表本身的1:n级联必须把该属性定义为:可空(long?=null)。
/// </remarks>
/// </summary>
public long? ParentId { get; set; }
/// <summary>
/// 【名称】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定产品类型的名称。
/// </remarks>
/// </summary>
public string Name { get; set; }
/// <summary>
/// 【菜单级别】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定产品类型的级别值(1`2)。
/// </remarks>
/// </summary>
public int Level { get; set; }
/// <summary>
/// 【激活?】
/// <remarks>
/// 摘要:
/// 获取/设置1个值false(默认值:禁用)/true(激活),该值指示产品类型实体的1个指定实例是否处于已经激活状态。
/// </remarks>
/// </summary>
public bool IsActive { get; set; }
/// <summary>
/// 【创建时间】
/// <remarks>
/// 摘要:
/// 获取/设置产品类型实体1个指定实例第1次被持久化到产品类型表中的时间。
/// </remarks>
/// </summary>
public DateTime CreatedDateTime { get; set; }
/// <summary>
/// 【创建时间】
/// <remarks>
/// 摘要:
/// 获取/设置产品类型实体1个指定实例最后1次被修改后,持久化到产品类型表中的时间。
/// </remarks>
/// </summary>
public DateTime UpdatedDateTime { get; set; }
#endregion
#region 属性--映射和级联构建
/// <summary>
/// 【子产品类型集】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定产品类型实例所对应的所有子产品类型实例。
/// 说明:
/// 构建同1产品类型表中父项与子项之间的1:n映射关系。
/// </remarks>
/// </summary>
public virtual ICollection<Category> ChildrenCollection { get; set; }
/// <summary>
/// 【产品集】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定产品类型实例所对应的所有产品实例。
/// 说明:
/// 产品类型与产品实体及其表之间的1:n映射关系。
/// </remarks>
/// </summary>
public virtual ICollection<Product> ProductCollection { get; set; }
#endregion
}
}
2.2 Data.Mapping.Catalog. CategoryBuilder
//Nuget
//Nuget--Microsoft.EntityFrameworkCore.SqlServer
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
//项目
using Core.Domain.Catalog;
namespace Data.Mapping.Catalog
{
/// <summary>
/// 【产品类型生成器--类】
/// <remarks>
/// 摘要:
/// 该类通过对“EntityFrameworkCore”中间件“IEntityTypeConfiguration<TEntity/>”泛型接口的“Configure”方法的定义,以实现把产品类型实体类及其属性成员相关约束规则及其级联关系定义,映射到产品类型表及其的相应字段上。
/// </remarks>
/// </summary>
public class CategoryBuilder : IEntityTypeConfiguration<Category>
{
#region 方法--IEntityTypeConfiguration<>
///<param name="builder">实体类型生成器实例,用于把当前程序中指定实体和属性所定义的约束规则,映射到数据库指定表及其字段上。</param>
/// <summary>
/// 【配置】
/// <remarks>
/// 摘要:
/// 该方法通过对“EntityFrameworkCore”中间件“IEntityTypeConfiguration<TEntity/>”泛型接口的“Configure”方法的定义,以实现把产品类型实体类及其属性成员相关约束规则及其级联关系定义,映射到产品类型表及其的相应字段上。
/// </remarks>
/// </summary>
public void Configure(EntityTypeBuilder<Category> builder)
{
//由于“EntityTypeBuilder<Category>”的参数已经泛型实例化,因此builder后不能再定义为:“builder.Entity<Category>().HasKey(category => category.Id);”。
//用户表及其字段约束规则,映射定义。
builder.HasKey(category => category.Id);
//说明:
// 1、“Microsoft SQL Server”数据库软件对同1个表之中的关联删除操作是不支持的,所以在同1个表的关联约束规则定义中,
//不能使用“.OnDelete(DeleteBehavior.ClientCascade)”在同1个表中构建关联删除关系,否则在通过Code-First模式生成数据库及其表时会出现逻辑异常。
// 2、“Microsoft SQL Server”数据库软件对两个不同表之间的关联删除操作是支持的,所以在两个不同表之间的关联约束规则定义中,
//可以能使用“.OnDelete(DeleteBehavior.ClientCascade)”在两个不同表之间构建关联删除关系。
// 3、默认的关联约束规则枚举实例为:DeleteBehavior.ClientSetNull,实则ClientCascade、Restrict也可以通过Code-First模式生成数据库及其表。
builder.HasMany(category => category.ChildrenCollection)
.WithOne()
.HasForeignKey(category => category.ParentId);
builder.Property(category => category.Name).IsRequired().HasMaxLength(255);
builder.Property(category => category.CreatedDateTime).IsRequired();
builder.Property(category => category.UpdatedDateTime).IsRequired();
}
#endregion
}
}
2.3 Services.Catalog. ICategoryService
using Core.Domain.Catalog;
namespace Services.Catalog
{
/// <summary>
/// 【产品类型服务--接口】
/// <remarks>
/// 摘要:
/// 通过继承于该接口具体实现类中的方法成员实现当前程序与产品类型表之间的CURD操作。
/// </remarks>
/// </summary>
public interface ICategoryService
{
/// <summary>
/// 【异步获取所有产品类型】
/// <remarks>
/// 摘要:
/// 获取产品类型的所有所有实例。
/// </remarks>
/// <returns>
/// 返回:
/// 产品类型的所有所有实例。
/// </returns>
/// </summary>
Task<IList<Category>> GetAllCatalogAsync();
/// <summary>
/// 【异步获取所有产品类型】
/// <remarks>
/// 摘要:
/// 以树型结构获取产品类型的所有所有实例。
/// </remarks>
/// <returns>
/// 返回:
/// 产品类型的树型结构的所有所有实例。
/// </returns>
/// </summary>
Task<IList<Category>> GetAllCatalogTreeAsync();
}
}
2.4 Services.Catalog. CategoryService
using Core.Caching;
using Core.Domain.Catalog;
using Data;
using Core.Extensions;
using Data.Extensions;
namespace Services.Catalog
{
/// <summary>
/// 【产品类型服务--类】
/// <remarks>
/// 摘要:
/// 通过类中的方法成员实现当前程序与产品类型表之间的CURD操作。
/// </remarks>
/// </summary>
public class CategoryService : ICategoryService
{
#region 拷贝构造方法与变量
private readonly IRepository<Category> _categoryRepository;
private readonly IStaticCacheManager _staticCacheManager;
/// <summary>
/// 【拷贝构建方法】
/// <remarks>
/// 摘要:
/// 依赖注入容器通过拷贝构造方法,实例化该类中的变量成员。
/// </remarks>
/// </summary>
public CategoryService(
IRepository<Category> categoryRepository,
IStaticCacheManager staticCacheManager)
{
_categoryRepository = categoryRepository;
_staticCacheManager = staticCacheManager;
}
#endregion
#region 方法--接口实现
/// <summary>
/// 【异步获取所有产品类型】
/// <remarks>
/// 摘要:
/// 获取产品类型的所有所有实例。
/// </remarks>
/// <returns>
/// 返回:
/// 产品类型的所有所有实例。
/// </returns>
/// </summary>
public async Task<IList<Category>> GetAllCatalogAsync()
{
return await _categoryRepository.GetEntitiesAsync(async () =>
{
return await _categoryRepository.Table.ToListAsync();
},
cache => _staticCacheManager.PrepareKeyForDefaultCache(EntityCacheDefaults<Category>.AllCacheKey));
}
/// <summary>
/// 【异步获取所有产品类型】
/// <remarks>
/// 摘要:
/// 以树型结构获取产品类型的所有所有实例。
/// </remarks>
/// <returns>
/// 返回:
/// 产品类型的树型结构的所有所有实例。
/// </returns>
/// </summary>
public async Task<IList<Category>> GetAllCatalogTreeAsync()
{
IList<Category> _categoryAll = await GetAllCatalogAsync();
IList<Category> _rootList = _categoryAll.Where(c => c.ParentId.Equals(null)).ToList();
List<Category> _categoryAllTree = new List<Category>();
foreach (var root in _rootList)
{
//通过递归方法,以树型结构构建1个根实例的所有子实例。
TreeExtension.TreeBuilder(_categoryAll.ToList(), root, root.Id);
_categoryAllTree.Add(root);
}
return _categoryAllTree;
}
#endregion
}
}
3 WebApi.Controllers. CategoryController
using Core.Domain.Catalog;
using Framework.Filters;
using Microsoft.AspNetCore.Mvc;
using Services.Catalog;
using WebApi.Extensions;
using WebApi.Models;
namespace WebApi.Controllers
{
[Route("[controller]/[action]")]
[ApiController]
//[Authorize(PermissionsPolicy.Name)]
[SaveLastActivity]
public class CategoryController : ControllerBase
{
#region 拷贝构造方法与变量
private readonly ICategoryService _categoryService;
/// <summary>
/// 【拷贝构建方法】
/// <remarks>
/// 摘要:
/// 依赖注入容器通过拷贝构造方法,实例化该类中的变量成员。
/// </remarks>
/// </summary>
public CategoryController(
ICategoryService categoryService)
{
_categoryService = categoryService;
}
#endregion
/// <summary>
/// 【产品类型表格--需权限】
/// </summary>
/// <remarks>
/// 摘要:
/// 产品类型的所有实例,为产品类型表格的渲染显示提供数据支撑。
/// </remarks>
/// <returns>
/// 返回:
/// 消息模型纪录的1个指定实例,该实例存储当前“Api”方法的执行操作结果,为客户端页面的渲染提供数据支撑。
/// </returns>
[HttpGet]
public async Task<MessageModel<IList<Category>>> Index()
{
IList<Category> _categoryAllList = await _categoryService.GetAllCatalogTreeAsync();
return MessageModel<IList<Category>>.GetSuccess("成功获取产品类型的所有实例!", _categoryAllList);
}
/// <summary>
/// 【产品类型下拉框控件的树型结构--需权限】
/// </summary>
/// <remarks>
/// 摘要:
/// 以树型结构进行存储产品类型的所有实例,为下拉框控件以树型结构渲染显示提供数据支撑。。
/// </remarks>
/// <returns>
/// 返回:
/// 以树型结构进行存储产品类型的所有实例。
/// </returns>
[HttpGet]
public async Task<MessageModel<List<SelectListTreeModel>>> GetSelectListTree()
{
List<SelectListTreeModel> _categorySelectListTreeModel = new List<SelectListTreeModel>();
_categorySelectListTreeModel.Add(new SelectListTreeModel()
{
Id = 0,
ParentId = 0,
Text = "无父级",
Last = true,
});
IList<Category> _categoryAllList = await _categoryService.GetAllCatalogAsync();
_categorySelectListTreeModel.AddRange(SelectListTreeExtension.SelectListTree(_categoryAllList.ToList()));
return MessageModel<List<SelectListTreeModel>>.GetSuccess("成功获取", _categorySelectListTreeModel);
}
}
}
对以上功能更为具体实现和注释见:230426_048shopDemo(以泛型方式定义的递归方法构建树型结构数据的2种实现方法)。文章来源地址https://www.toymoban.com/news/detail-426453.html
到了这里,关于第63章 以泛型方式定义的递归方法构建树型结构数据的2种实现方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!