利用代碼生成工具生成基于ABP框架的代碼

在前面隨筆喧务,我介紹了整個ABP優(yōu)化過框架的分層模型,包括盡量簡化整個ABP框架的各個層的關(guān)系缺虐,以及納入一些基類的輔助處理芜壁,使得我們對應(yīng)業(yè)務(wù)分層類或者接口盡可能減少代碼,并具有生產(chǎn)環(huán)境所需要的基類接口,通過我對整個ABP框架模型的分析慧妄,我們可以結(jié)合代碼生成工具Database2Sharp來生成對應(yīng)分層的代碼顷牌,該工具后臺具備數(shù)據(jù)庫表所需要的一切字段信息和關(guān)系信息,因此我們確定好邏輯關(guān)系就可以生成對應(yīng)分層的代碼塞淹。本篇隨筆介紹代碼生成工具Database2Sharp生成基于ABP框架的分層代碼過程窟蓝。

1)ABP框架回顧

ABP框架主要還是基于領(lǐng)域驅(qū)動的理念來構(gòu)建整個架構(gòu)的,其中領(lǐng)域驅(qū)動包含的概念有 域?qū)ο驟ntities饱普、倉儲對象Repositories运挫、域服務(wù)接口層Domain Services、域事件Domain Events套耕、應(yīng)用服務(wù)接口Application Services谁帕、數(shù)據(jù)傳輸對象DTOs等。

以下是ABP初始框架的各個分層的信息冯袍,它主要是分為下面幾個項目分層匈挖。

Application應(yīng)用層:應(yīng)用層提供一些應(yīng)用服務(wù)(Application Services)方法供展現(xiàn)層調(diào)用。一個應(yīng)用服務(wù)方法接收一個DTO(數(shù)據(jù)傳輸對象)作為輸入?yún)?shù)康愤,使用這個輸入?yún)?shù)執(zhí)行特定的領(lǐng)域?qū)硬僮骼苎⒏鶕?jù)需要可返回另一個DTO。

Core領(lǐng)域核心層征冷,領(lǐng)域?qū)泳褪菢I(yè)務(wù)層择膝,是一個項目的核心,所有業(yè)務(wù)規(guī)則都應(yīng)該在領(lǐng)域?qū)訉崿F(xiàn)资盅。這個項目里面调榄,除了定義所需的領(lǐng)域?qū)嶓w類外,其實可以定義我們自己的自定義的倉儲對象(類似DAL/IDAL)呵扛,以及定義自己的業(yè)務(wù)邏輯層(類似BLL/IBLL)每庆,以及基于AutoMapper映射規(guī)則等內(nèi)容。

EntityFrameworkCore 實體框架核心層今穿,這個項目不需要修改太多內(nèi)容缤灵,只需要在DbContext里面加入對應(yīng)領(lǐng)域?qū)ο蟮膫}儲對象即可。

Migrator數(shù)據(jù)遷移層蓝晒,這個是一個輔助創(chuàng)建的控制臺程序項目腮出,如果基于DB First,我們可以利用它來創(chuàng)建我們項目的初始化數(shù)據(jù)庫芝薇。

Web.Core Web核心層胚嘲,基于Web或者Web API的核心層,提供了對身份登陸驗證的基礎(chǔ)處理洛二,沒有其他內(nèi)容馋劈。

Web.Core.Host Web API的宿主層攻锰,也是動態(tài)發(fā)布Web API的核心內(nèi)容,另外在Web API里面整合了Swagger妓雾,使得我們可以方便對Web API的接口進(jìn)行調(diào)試娶吞。

Tests 單元測試層,這個提供了一些應(yīng)用層對象的模擬測試械姻,其中測試的數(shù)據(jù)庫使用的是Entity Framework 的內(nèi)存數(shù)據(jù)庫妒蛇,不影響實際數(shù)據(jù)庫內(nèi)容。

經(jīng)過我進(jìn)行簡化和優(yōu)化處理的框架項目結(jié)構(gòu)如下所示楷拳。

image

以上是VS里面解決方案的項目結(jié)構(gòu)绣夺,我根據(jù)項目之間的關(guān)系,整理了一個架構(gòu)的圖形唯竹,如下所示乐导。

image

上圖是以字典模塊為介紹, 其中橘紅色的部分就是我們?yōu)楦鱾€分層需要根據(jù)數(shù)據(jù)庫構(gòu)建對應(yīng)的類或者接口文件浸颓。

例如對于01-Core模塊層物臂,需要增加文件

image

對于03-Application.Common模塊來說,需要增加DTO和應(yīng)用服務(wù)層接口文件

image

而對于04-Application應(yīng)用層來說产上,需要增加對應(yīng)的接口實現(xiàn)文件

image

而05棵磷、06、07模塊晋涣,我們不需要加入任何文件仪媒,08-Caller層加入對WebAPI的遠(yuǎn)程調(diào)用封裝類,給Winform谢鹊、WPF/UWP算吩、控制臺程序等調(diào)用。

image

一個模塊的變化佃扼,都會導(dǎo)致在上面各個分層之間增加對應(yīng)的文件偎巢,這樣的架構(gòu)確定后,我們就可以根據(jù)對應(yīng)的類生成規(guī)則進(jìn)行生成接口兼耀。

2)利用代碼生成工具生成分層代碼

在前面隨筆《代碼生成工具Database2Sharp的架構(gòu)介紹》中压昼,我介紹了整個代碼生成工具的架構(gòu)信息,因此我們用代碼生成工具生成架構(gòu)代碼的時候瘤运,可以利用整個數(shù)據(jù)庫表的信息和關(guān)系信息來處理窍霞。

image

通過整合相關(guān)的生成規(guī)則,我們可以增加對應(yīng)的ABP框架代碼的生成拯坟,如下代碼生成工具界面所示但金。

image
image

最終根據(jù)根據(jù)選擇數(shù)據(jù)庫表信息,一鍵生成相關(guān)ABP架構(gòu)分層代碼郁季,文件結(jié)構(gòu)如下所示傲绣。

image

對比前面項目的介紹掠哥,我們可以看到各個分層的類代碼是完全一致的。如對于領(lǐng)域?qū)油核校吮砻Q標(biāo)記、字段信息和引用外鍵的對象塞琼。

    /// <summary>
    /// 通用字典明細(xì)項目信息菠净,領(lǐng)域?qū)ο?    /// </summary>
    [Table("TB_DictData")]
    public class DictData : FullAuditedEntity<string>
    { 
        /// <summary>
        /// 默認(rèn)構(gòu)造函數(shù)(需要初始化屬性的在此處理)
        /// </summary>
        public DictData()
        {
        }

        #region Property Members
        
        /// <summary>
        /// 字典大類
        /// </summary>
        //[Required]
        public virtual string DictType_ID { get; set; }

        /// <summary>
        /// 字典名稱
        /// </summary>
        //[Required]
        public virtual string Name { get; set; }

        /// <summary>
        /// 字典值
        /// </summary>
        public virtual string Value { get; set; }

        /// <summary>
        /// 備注
        /// </summary>
        public virtual string Remark { get; set; }

        /// <summary>
        /// 排序
        /// </summary>
        public virtual string Seq { get; set; }

        /// <summary>
        /// 字典大類
        /// </summary>
        [ForeignKey("DictType_ID")]
        public virtual DictType DictType { get; set; }
        #endregion

    }

對于DTO文件,我們看看代碼信息

    /// <summary>
    /// 通用字典明細(xì)項目信息彪杉,DTO對象
    /// </summary>
    public class DictDataDto
    { 
        /// <summary>
        /// 默認(rèn)構(gòu)造函數(shù)(需要初始化屬性的在此處理)
        /// </summary>
        public DictDataDto()
        {
        }

        #region Property Members
        
        /// <summary>
        /// 字典大類
        /// </summary>
        public virtual string DictType_ID { get; set; }

        /// <summary>
        /// 字典名稱
        /// </summary>
        public virtual string Name { get; set; }

        /// <summary>
        /// 字典值
        /// </summary>
        //[Required]
        public virtual string Value { get; set; }

        /// <summary>
        /// 備注
        /// </summary>
        public virtual string Remark { get; set; }

        /// <summary>
        /// 排序
        /// </summary>
        public virtual string Seq { get; set; }

        #endregion

    }

    /// <summary>
    /// 創(chuàng)建通用字典明細(xì)項目信息毅往,DTO對象
    /// </summary>
    public class CreateDictDataDto : DictDataDto
    {
    }

    /// <summary>
    /// 用于根據(jù)條件分頁查詢,DTO對象
    /// </summary>
    public class DictDataPagedDto : PagedResultRequestDto
    {
        public DictDataPagedDto() { }

        /// <summary>
        /// 參數(shù)化構(gòu)造函數(shù)
        /// </summary>
        /// <param name="skipCount">跳過的數(shù)量</param>
        /// <param name="resultCount">最大結(jié)果集數(shù)量</param>
        public DictDataPagedDto(int skipCount, int resultCount)
        {
            this.SkipCount = skipCount;
            this.MaxResultCount = resultCount;
        }

        /// <summary>
        /// 使用分頁信息進(jìn)行初始化SkipCount 和 MaxResultCount
        /// </summary>
        /// <param name="pagerInfo">分頁信息</param>
        public DictDataPagedDto(PagerInfo pagerInfo)
        {
            if (pagerInfo != null)
            {
                //默認(rèn)設(shè)置
                var pageSize = pagerInfo.PageSize > 0 ? pagerInfo.PageSize : 50;
                var pageIndex = pagerInfo.CurrenetPageIndex > 0 ? pagerInfo.CurrenetPageIndex : 1;

                this.SkipCount = pageSize * (pageIndex - 1);
                this.MaxResultCount = pageSize;
            }
        }

        #region Property Members
        
        /// <summary>
        /// 字典大類
        /// </summary>
        public virtual string DictType_ID { get; set; }

        /// <summary>
        /// 字典名稱
        /// </summary>
        public virtual string Name { get; set; }

        /// <summary>
        /// 字典值
        /// </summary>
        public virtual string Value { get; set; }

        /// <summary>
        /// 備注
        /// </summary>
        public virtual string Remark { get; set; }

        /// <summary>
        /// 排序
        /// </summary>
        public virtual string Seq { get; set; }


        #endregion
    }

DTO的映射文件代碼生成如下

    /// <summary>
    /// 通用字典明細(xì)項目信息派近,映射文件
    /// </summary>
    public class DictDataMapProfile : Profile  
    {
        public DictDataMapProfile()
        {
            CreateMap<DictDataDto, DictData>();
            CreateMap<DictData, DictDataDto>();
            CreateMap<CreateDictDataDto, DictData>();
        }
    }

應(yīng)用服務(wù)層接口實現(xiàn)代碼如下所示攀唯。

    /// <summary>
    /// 通用字典明細(xì)項目信息,應(yīng)用層服務(wù)接口實現(xiàn)
    /// </summary>
    [AbpAuthorize]
    public class DictDataAppService : MyAsyncServiceBase<DictData, DictDataDto, string, DictDataPagedDto, CreateDictDataDto, DictDataDto>, IDictDataAppService
    {
        private readonly IRepository<DictData, string> _repository;

        public DictDataAppService(IRepository<DictData, string> repository) : base(repository)
        {
            _repository = repository;
        }

        /// <summary>
        /// 自定義條件處理
        /// </summary>
        /// <param name="input">查詢條件Dto</param>
        /// <returns></returns>
        protected override IQueryable<DictData> CreateFilteredQuery(DictDataPagedDto input)
        {
            return base.CreateFilteredQuery(input)
                .WhereIf(!DictType_ID.IsNullOrWhiteSpace(), t => t.DictType_ID.Contains(input.DictType_ID))
                .WhereIf(!Name.IsNullOrWhiteSpace(), t => t.Name.Contains(input.Name))
                .WhereIf(!Value.IsNullOrWhiteSpace(), t => t.Value.Contains(input.Value))
                .WhereIf(!Remark.IsNullOrWhiteSpace(), t => t.Remark.Contains(input.Remark))
                .WhereIf(!Seq.IsNullOrWhiteSpace(), t => t.Seq.Contains(input.Seq));
        }

        /// <summary>
        /// 自定義排序處理
        /// </summary>
        /// <param name="query">可查詢LINQ</param>
        /// <param name="input">查詢條件Dto</param>
        /// <returns></returns>
        protected override IQueryable<DictData> ApplySorting(IQueryable<DictData> query, DictDataPagedDto input)
        {
            return base.ApplySorting(query, input);

            //示例代碼
            //先按字典類型排序渴丸,然后同一個字典類型下的再按Seq排序
            //return base.ApplySorting(query, input).OrderBy(s=>s.DictType_ID).ThenBy(s => s.Seq);
        }
    }

ApiCaller分層的代碼實現(xiàn)如下所示侯嘀。

    /// <summary>
    /// 通用字典明細(xì)項目信息的Web API調(diào)用處理
    /// </summary>
    public class DictDataApiCaller : AsyncCrudApiCaller<DictDataDto, string, DictDataPagedDto, CreateDictDataDto, DictDataDto>, IDictDataAppService
    {
        /// <summary>
        /// 提供單件對象使用
        /// </summary>
        public static DictDataApiCaller Instance
        {
            get
            {
                return Singleton<DictDataApiCaller>.Instance;
            }
        }

        /// <summary>
        /// 默認(rèn)構(gòu)造函數(shù)
        /// </summary>
        public DictDataApiCaller()
        {
            this.DomainName = "DictData";//指定域?qū)ο竺Q,用于組裝接口地址
        }

    }

這些信息是根據(jù)數(shù)據(jù)庫對應(yīng)字段信息和關(guān)系信息進(jìn)行批量生成谱轨,我們可以在這基礎(chǔ)上進(jìn)行一定的調(diào)整戒幔,以及增加自己的業(yè)務(wù)接口,那么就非常方便了土童。

利用代碼生成工具的數(shù)據(jù)庫元數(shù)據(jù)诗茎,結(jié)合模板引擎NVelocity,我們可以為我們的項目框架代碼快速生成提供了一個快速有效献汗、統(tǒng)一標(biāo)準(zhǔn)的生成方式敢订,大大提高了生產(chǎn)效率。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末罢吃,一起剝皮案震驚了整個濱河市楚午,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌刃麸,老刑警劉巖醒叁,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異泊业,居然都是意外死亡把沼,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進(jìn)店門吁伺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饮睬,“玉大人,你說我怎么就攤上這事篮奄±Τ睿” “怎么了割去?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長昼丑。 經(jīng)常有香客問我呻逆,道長,這世上最難降的妖魔是什么菩帝? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任咖城,我火速辦了婚禮,結(jié)果婚禮上呼奢,老公的妹妹穿的比我還像新娘宜雀。我一直安慰自己,他們只是感情好握础,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布辐董。 她就那樣靜靜地躺著,像睡著了一般禀综。 火紅的嫁衣襯著肌膚如雪简烘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天菇存,我揣著相機(jī)與錄音夸研,去河邊找鬼。 笑死依鸥,一個胖子當(dāng)著我的面吹牛亥至,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播贱迟,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼姐扮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了衣吠?” 一聲冷哼從身側(cè)響起茶敏,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎缚俏,沒想到半個月后惊搏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡忧换,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年恬惯,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片亚茬。...
    茶點(diǎn)故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡酪耳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出刹缝,到底是詐尸還是另有隱情碗暗,我是刑警寧澤颈将,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站言疗,受9級特大地震影響晴圾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜洲守,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一疑务、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧梗醇,春花似錦、人聲如沸撒蟀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽保屯。三九已至手负,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間姑尺,已是汗流浹背竟终。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留切蟋,地道東北人统捶。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像柄粹,于是被迫代替她去往敵國和親喘鸟。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評論 2 361

推薦閱讀更多精彩內(nèi)容