一、前言

上一篇大概说了下abp通用树形模块如何使用,本篇主要分析下设计思路。

日常开发中会用到很多树状结构的数据,比如:产品的多级分类、省市区县,大多数系统也会用到类似“通用字典/数据字典”的功能,为系统各个地方提下拉框选择的数据源。abp提供了一个模块化系统,只要按它的约定就可以实现一个通用的树形数据的模块,这样公司的多个系统都可以使用,也可以用类似nuget的方式提供给别人使用。

先列举下它的功能

  1. 通过nuget方便安装和升级
  2. 配置简单
  3. 默认已经提供“通用字典”功能
  4. 实体、管理器、应用服务都是抽象类,结合泛型 狠容易扩展实现自己的树形结构

二、必备知识

这不是abp入门级的文章,是探讨系统模块化开发的一种思路。所以要求对abp有经验,完整看过abp文档,对涉及到的模块、依赖注入、启动配置、权限、菜单、本地化等等概念有清晰的认识

三、包和源码

源码地址:https://github.com/bxjg1987/abpGeneralModulesnuget:Install-Package BXJG.GeneralTree -Version 1.0.2
在线地址:  http://test.cqsifang.com/    账号密码:admin  zlj.com    (别胡来,拜托...)
源码仓库中还有通用的文件模块、附件模块,后期会讲讲;nuget搜索bxjg可以找到这几个相关的包

四、总体设计

为了便于说明,我们以常见的产品无限级分类来说明,所有树形结构数据有这么几个特点:

  • 有个ParentId属性指向父级节点,
  • 有个code,数据格式类似:00001.00001,一个是能简单体现产品分类节点的层级结构,二个是方便将来查询某个分类及其后代分类下的所有产品 where categoryCode like '00001%
  • 当然还有Name属性

上面说了定义实体类的关键点,然后我们需要提供一个对应的Manager(这个是abp定义领域服务的套路),来主负责CRUD和节点间的移动操作,其实最麻烦的就是自动处理code,新增和修改时要根据所属父节点的code自动生成子节点的code,移动时就更复杂了,要重新计算当前节点及其兄弟节点及其所有后代节点的code,这还涉及到目标节点和源节点。为了便于扩展,上面的领域服务还得将被处理的实体泛型话,文章后续会具体说明

最后按abp套路我们还需要提一个应用服务,它核心操作就是调用上面提供的Manager做节点的crud操作,在此基础上按abp的套路应该提供权限验证、处理实体与dto之间的转换。同理为了便于将来模块的使用方进行扩展,我们应该应用抽象类和泛型的威力,文章后续会具体说明

最后我们如何提供Repository呢?参考abp zero的思路,我们将这个遗留到调用方来定义。在我们的领域服务Manager和应用服务AppService中都是通过依赖注入注入的IRepository<TEntity,TKey>

核心的3个东东定义好后,我们分别实现一个默认类,实现一个“通用字典”,将来调用方可以继承我们的类并提供泛型参数来实现他们自己的树形结构。
另外我们将所有的代码放在一个项目中,因为模块足够小,功能少  这样调用方用起来更方便

注意一点,上面以产品分类来说明只是便于理解,既然要提供扩展能力,设计时只能从抽象的角度来看待树形数据,否则设计出来的东西很容易最后发现不够抽象,此废话只可意会不可言传

最后是abp相关的:本地化定义、模块定义及依赖关系、权限提供器、菜单提供器、动态webapi的处理

五、实体类

实体类我这里只是说明,具体源码请访问顶部的github

GeneralTreeEntity<TEntity>

它定义了树形结构数据的通用树形,Id、Code、Name、Parent等
泛型TEntity是子节点的类型,由于是树,实际上也是父节点的类型,这种设计是方便将来模块使用方在实现自定义的树形结构时拿到的Parent树形和Children树形,是他们自己定义的类型
主键Id属性,我定死了long类型,其实也可以使用泛型的TKey,思来想去简单起见直接定死吧

public class GeneralTreeEntity : GeneralTreeEntity<GeneralTreeEntity>

为了提供我们默认实现的“通用字典”功能,我们定义一个默认的子类GeneralTreeEntity,将来系统中那些简单的数据可以直接使用它
IsSysDefine表示此节点是否是系统预定义的,将来不允许删除
IsTree是否是树形,将来可能会用到,因为某些下拉框数据可能不需要多层次的,比如:民族,学历

六、领域服务

按套路我们提供一个抽象的领域服务Manager类,和一个为了实现“通用字典”功能的默认实现

public abstract class GeneralTreeManager<TEntity> : DomainService  where TEntity : GeneralTreeEntity<TEntity>

它的主要职责总体设计也说了一嘴,完成crud和move移动操作,难点是处理code的自动生成,尤其是移动节点时,节点原来位置之后的其它节点及其后代节点、目标节点之后的其它节点及其后代节点的code的生成
内部对数据的操作直接注入IRepository<TEntity, long>,因此按abp的套路,默认情况下使用EF时,调用方只需要在他的DBContext中顶一个DBSet就可以了

public class GeneralTreeManager : GeneralTreeManager<GeneralTreeEntity>

实现“通用数据字典”的默认领域服类,因为核心功能父类基本都完成了。所以几乎没代码

七、应用服务

按套路一个抽象类,一个默认实现类,内部核心操作是上面提供的Manager来完成的,应用服务主要是出去权限和dto之间的转换

public  class GeneralTreeAppServiceBase< TEntity,  TDto, TEditDto> : ApplicationService,     IGeneralTreeAppServiceBase<TDto, TEditDto>
        where TEntity : GeneralTreeEntity<TEntity>
        where TDto : GeneralTreeGetTreeNodeBaseDto<TDto>, new()
        where TEditDto : GeneralTreeNodeEditBaseDto

另外它提供了一些通用方法,一个树形数据通常在3个地方呗使用,以产品分类为例,在产品分类的管理页面、在产品的搜索栏应该提供产品类别的选择、在产品编辑页面应该有个下拉框选择当前产品所属类别,其它树形数据都有类似的场景,因此抽象的应用服务处理了这部分功能

public class GeneralTreeAppService : GeneralTreeAppServiceBase< GeneralTreeEntity, GeneralTreeDto, GeneralTreeEditDt, GeneralTreeManager>,IGeneralTreeAppService

按套路我们为“通用字典”提供了一个默认的实现类

应用服务中的DTO定义、应用服务接口我这里省略了,这是abp的常规套路,请看源码

八、abp相关套路:模块、本地化、权限、菜单

模块和本地化就不说了,abp的常规套路
由于我们提供了“通用字典”,默认情况下是可以直接用作你项目的,调用方完全可以按abp的套路在自己的模块中来配置权限和菜单,但是默认我们的模块也提供了,调用方完全可以在主机的PermissionProvicer和NavigationProvider的合适的位置配置权限和菜单。参考GeneralTreeModuleConfig源码

九、总结

abp本身提供了模块化方式,如果运用得当我们的系统可以由很多小模块组成,将来更容易维护、升级和复用
就好比我们目前提供的通用树,如果你使用的是abp,那么完全可以拿去就用,类似的“通用附件”模块,因为我们的多个系统或者同一个系统都可能会用到附件的功能,到处复制代码是下下策。

asp.net abp模块化开发之通用树2:设计思路及源码解析的更多相关文章

  1. abp模块化开发之通用树1:基本使用

    一.概述 有些功能在单个项目或多个项目被重复使用,比如:附件,同一个系统中的多个模块都可能使用到,不同项目也有需要.再比如:有无限级分类的树形功能,区域.产品分类.数据字典等.最简单粗暴的办法是直接复 ...

  2. Asp.net Mvc模块化开发之分区扩展框架

    对于一个企业级项目开发,模块化是非常重要的. 默认Mvc框架的AreaRegistration对模块化开发真的支持很好吗?真的有很多复杂系统在使用默认的分区开发的吗?我相信大部分asp.net的技术团 ...

  3. 初识ABP vNext(9):ABP模块化开发-文件管理

    Tips:本篇已加入系列文章阅读目录,可点击查看更多相关文章. 目录 前言 开始 创建模块 模块开发 应用服务 运行模块 单元测试 模块使用 最后 前言 在之前的章节中介绍过ABP扩展实体,当时在用户 ...

  4. Asp.net Mvc模块化开发系列(目录)

    模块化开发是非常重要的,模块化开发是个系统性问题,为此我觉得有必须要写一个系列的文章才能基本说的清楚 那又为什么要写一个目录呢? 其一.是对我昨天承诺写一个系列新的文章的回应 其二.是先写出一个大纲, ...

  5. C#/ASP.NET MVC微信公众号接口开发之从零开发(四) 微信自定义菜单(附源码)

    C#/ASP.NET MVC微信接口开发文章目录: 1.C#/ASP.NET MVC微信公众号接口开发之从零开发(一) 接入微信公众平台 2.C#/ASP.NET MVC微信公众号接口开发之从零开发( ...

  6. 【源码解析】凭什么?spring boot 一个 jar 就能开发 web 项目

    问题 为什么开发web项目,spring-boot-starter-web 一个jar就搞定了?这个jar做了什么? 通过 spring-boot 工程可以看到所有开箱即用的的引导模块 spring- ...

  7. abp vnext2.0核心组件之领域实体组件源码解析

    接着abp vnext2.0核心组件之模块加载组件源码解析和abp vnext2.0核心组件之.Net Core默认DI组件切换到AutoFac源码解析集合.Net Core3.1,基本环境已经完备, ...

  8. asp微信支付代码证书文件post_url.aspx和post_url.aspx.cs源码下载

    很多朋友在网上找的asp支付代码中都没有这两个证书文件,只能是用别人的,但是如果别人把他的网站这个文件删了,你的支付也就不能用了,今天我就把大家需要的这两个asp微信支付代码证书文件post_url. ...

  9. c++实现游戏开发中常用的对象池(含源码)

    c++实现游戏开发中常用的对象池(含源码) little_stupid_child2017-01-06上传   对象池的五要素: 1.对象集合 2.未使用对象索引集合 3.已使用对象索引集合 4.当前 ...

随机推荐

  1. 如梦令编程语言发布 (RML)

    如梦令编程语言是在Rebol语言核心语法的基础上,做了一些自以为是的修改而来.谨以此为Rebol语法的传承,略尽绵薄之力. 基本概念 如梦令语言分属Lisp语系,代码本身是一个层层嵌套的Token列表 ...

  2. STM8 ADC1连续模式切换通道干扰问题的解决

    初始化ADC1: void Init_ADC(void){ GPIO_Init(GPIOD, (GPIO_Pin_TypeDef)(GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_5|G ...

  3. FFMPEG学习----使用SDL播放PCM数据

    参考雷神的代码: /** * 最简单的SDL2播放音频的例子(SDL2播放PCM) * Simplest Audio Play SDL2 (SDL2 play PCM) * * 本程序使用SDL2播放 ...

  4. Affinity Propagation Demo1学习

    利用AP算法进行聚类: 首先导入需要的包: from sklearn.cluster import AffinityPropagation from sklearn import metrics fr ...

  5. 2020你还不会Java8新特性?

    Java8(1)新特性介绍及Lambda表达式 前言: 跟大娃一块看,把原来的电脑拿出来放中间看视频用 --- 以后会有的课程 难度 深入Java 8 难度1 并发与netty 难度3 JVM 难度4 ...

  6. 对权值线段树剪枝的误解--以HDU6703为例

    引子 对hdu6703,首先将问题转化为"询问一个排列中大于等于k的值里,下标超过r的最小权值是多少" 我们采用官方题解中的做法:权值线段树+剪枝 对(a[i],i)建线段树,查询 ...

  7. Dubbo 服务 IP 注册错误踩坑经历

    个人博客地址 studyidea.cn,点击查看更多原创文章 踩坑 公司最近新建一个机房,需要将现有系统同步部署到新机房,部署完成之后,两地机房同时对提供服务.系统架构如下图: 这个系统当前对外采用 ...

  8. 深入JVM垃圾回收机制,值得你收藏

    JVM可以说是为了Java开发人员屏蔽了很多复杂性,让Java开发的变的更加简单,让开发人员更加关注业务而不必关心底层技术细节,这些复杂性包括内存管理,垃圾回收,跨平台等,今天我们主要看看JVM的垃圾 ...

  9. 【python-leetcode713-双指针】乘积小于k的子数组

    问题描述: 给定一个正整数数组 nums. 找出该数组内乘积小于 k 的连续的子数组的个数. 示例 1: 输入: nums = [10,5,2,6], k = 100输出: 8解释: 8个乘积小于10 ...

  10. javascirpt获取随机数

    /* getran(min, max, n): 获取min与max之间的随机数 n: n保留浮点数数量 */ function getran(min, max, n){ return Number(( ...