1. 程式人生 > >asp.net abp模組化開發之通用樹2:設計思路及原始碼解析

asp.net abp模組化開發之通用樹2:設計思路及原始碼解析

一、前言

 

 

上一篇大概說了下abp通用樹形模組如何使用,本篇主要分析下設計思路。

日常開發中會用到很多樹狀結構的資料,比如:產品的多級分類、省市區縣,大多數系統也會用到類似“通用字典/資料字典”的功能,為系統各個地方提下拉框選擇的資料來源。abp提供了一個模組化系統,只要按它的約定就可以實現一個通用的樹形資料的模組,這樣公司的多個系統都可以使用,也可以用類似nuget的方式提供給別人使用。

先列舉下它的功能

  1. 通過nuget方便安裝和升級
  2. 配置簡單
  3. 預設已經提供“通用字典”功能
  4. 實體、管理器、應用服務都是抽象類,結合泛型 狠容易擴充套件實現自己的樹形結構

 

 

 

 

二、必備知識

這不是abp入門級的文章,是探討系統模組化開發的一種思路。所以要求對abp有經驗,完整看過abp文件,對涉及到的模組、依賴注入、啟動配置、許可權、選單、本地化等等概念有清晰的認識

 

三、包和原始碼

原始碼地址:https://github.com/bxjg1987/abpGeneralModules
nuget: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,那麼完全可以拿去就用,類似的“通用附件”模組,因為我們的多個系統或者同一個系統都可能會用到附件的功能,到處複製程式碼是下下策。

&n