第63章 以泛型方式定义的递归方法构建树型结构数据的2种实现方法

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);

            }

        }

    }

}

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,实则ClientCascadeRestrict也可以通过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种实现方法)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
泛型定义和使用方法如下所示: 1. 定义含有泛型方法泛型定义方法的修饰符和返回值类型之间,格式为:修饰符 <泛型> 返回值类型 方法名(参数列表(使用泛型)){ 方法体; } 在调用含有泛型方法时,需要确定泛型的具体数据类型,传递什么类型的参数,泛型就是什么类型。 2. 示例代码: ```java public class GenericMethod { public <E> void method01(E e){ System.out.println(e); } } ``` 3. 泛型类的实现与使用: 泛型类的定义格式为:public class <T>{} 泛型接口的定义格式为:public interface <T>{} 泛型方法定义格式为:public <T> method(T element){return element;} 4. 泛型方法的声明: 泛型方法的声明需要在修饰符与返回值类型之间添加泛型标识符 <T,E,...>,只有这格式声明的方法才是泛型方法泛型标识符可以是任意类型,常见的如T,E,K,V等。 5. 泛型方法的使用: 普通泛型方法的使用与普通方法相同,根据方法定义传递相应的参数即可。 综上所述,泛型定义和使用方法包括定义含有泛型方法泛型类的实现与使用、泛型方法的声明和泛型方法的使用。 #### 引用[.reference_title] - *1* [泛型定义与使用](https://blog.csdn.net/Liamcsl/article/details/114438049)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [泛型定义及使用](https://blog.csdn.net/qq_45036591/article/details/104034126)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [JAVA泛型-泛型方法定义和使用](https://blog.csdn.net/qq_39505245/article/details/120925331)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值