在前面隨筆喧务,我介紹了整個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)如下所示楷拳。
以上是VS里面解決方案的項目結(jié)構(gòu)绣夺,我根據(jù)項目之間的關(guān)系,整理了一個架構(gòu)的圖形唯竹,如下所示乐导。
上圖是以字典模塊為介紹, 其中橘紅色的部分就是我們?yōu)楦鱾€分層需要根據(jù)數(shù)據(jù)庫構(gòu)建對應(yīng)的類或者接口文件浸颓。
例如對于01-Core模塊層物臂,需要增加文件
對于03-Application.Common模塊來說,需要增加DTO和應(yīng)用服務(wù)層接口文件
而對于04-Application應(yīng)用層來說产上,需要增加對應(yīng)的接口實現(xiàn)文件
而05棵磷、06、07模塊晋涣,我們不需要加入任何文件仪媒,08-Caller層加入對WebAPI的遠(yuǎn)程調(diào)用封裝類,給Winform谢鹊、WPF/UWP算吩、控制臺程序等調(diào)用。
一個模塊的變化佃扼,都會導(dǎo)致在上面各個分層之間增加對應(yīng)的文件偎巢,這樣的架構(gòu)確定后,我們就可以根據(jù)對應(yīng)的類生成規(guī)則進(jìn)行生成接口兼耀。
2)利用代碼生成工具生成分層代碼
在前面隨筆《代碼生成工具Database2Sharp的架構(gòu)介紹》中压昼,我介紹了整個代碼生成工具的架構(gòu)信息,因此我們用代碼生成工具生成架構(gòu)代碼的時候瘤运,可以利用整個數(shù)據(jù)庫表的信息和關(guān)系信息來處理窍霞。
通過整合相關(guān)的生成規(guī)則,我們可以增加對應(yīng)的ABP框架代碼的生成拯坟,如下代碼生成工具界面所示但金。
最終根據(jù)根據(jù)選擇數(shù)據(jù)庫表信息,一鍵生成相關(guān)ABP架構(gòu)分層代碼郁季,文件結(jié)構(gòu)如下所示傲绣。
對比前面項目的介紹掠哥,我們可以看到各個分層的類代碼是完全一致的。如對于領(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)效率。