在前面隨筆,我介紹了整個ABP優化過框架的分層模型,包括盡量簡化整個ABP框架的各個層的關系,以及納入一些基類的輔助處理,使得我們對應業務分層類或者接口盡可能減少代碼,并具有生產環境所需要的基類接口,通過我對整個ABP框架模型的分析,我們可以結合代碼生成工具Database2Sharp來生成對應分層的代碼,該工具后臺具備數據庫表所需要的一切字段信息和關系信息,因此我們確定好邏輯關系就可以生成對應分層的代碼。本篇隨筆介紹代碼生成工具Database2Sharp生成基于ABP框架的分層代碼過程。
1)ABP框架回顧
ABP框架主要還是基于領域驅動的理念來構建整個架構的,其中領域驅動包含的概念有 域對象Entities、倉儲對象Repositories、域服務接口層Domain Services、域事件Domain Events、應用服務接口Application Services、數據傳輸對象DTOs等。
以下是ABP初始框架的各個分層的信息,它主要是分為下面幾個項目分層。
Application應用層:應用層提供一些應用服務(Application Services)方法供展現層調用。一個應用服務方法接收一個DTO(數據傳輸對象)作為輸入參數,使用這個輸入參數執行特定的領域層操作,并根據需要可返回另一個DTO。
Core領域核心層,領域層就是業務層,是一個項目的核心,所有業務規則都應該在領域層實現。這個項目里面,除了定義所需的領域實體類外,其實可以定義我們自己的自定義的倉儲對象(類似DAL/IDAL),以及定義自己的業務邏輯層(類似BLL/IBLL),以及基于AutoMapper映射規則等內容。
EntityFrameworkCore 實體框架核心層,這個項目不需要修改太多內容,只需要在DbContext里面加入對應領域對象的倉儲對象即可。
Migrator數據遷移層,這個是一個輔助創建的控制臺程序項目,如果基于DB First,我們可以利用它來創建我們項目的初始化數據庫。
Web.Core Web核心層,基于Web或者Web API的核心層,提供了對身份登陸驗證的基礎處理,沒有其他內容。
Web.Core.Host Web API的宿主層,也是動態發布Web API的核心內容,另外在Web API里面整合了Swagger,使得我們可以方便對Web API的接口進行調試。
Tests 單元測試層,這個提供了一些應用層對象的模擬測試,其中測試的數據庫使用的是Entity Framework 的內存數據庫,不影響實際數據庫內容。
經過我進行簡化和優化處理的框架項目結構如下所示。
以上是VS里面解決方案的項目結構,我根據項目之間的關系,整理了一個架構的圖形,如下所示。
上圖是以字典模塊為介紹, 其中橘紅色的部分就是我們為各個分層需要根據數據庫構建對應的類或者接口文件。
例如對于01-Core模塊層,需要增加文件
對于03-Application.Common模塊來說,需要增加DTO和應用服務層接口文件
而對于04-Application應用層來說,需要增加對應的接口實現文件
而05、06、07模塊,我們不需要加入任何文件,08-Caller層加入對WebAPI的遠程調用封裝類,給Winform、WPF/UWP、控制臺程序等調用。
一個模塊的變化,都會導致在上面各個分層之間增加對應的文件,這樣的架構確定后,我們就可以根據對應的類生成規則進行生成接口。
2)利用代碼生成工具生成分層代碼
在前面隨筆《代碼生成工具Database2Sharp的架構介紹》中,我介紹了整個代碼生成工具的架構信息,因此我們用代碼生成工具生成架構代碼的時候,可以利用整個數據庫表的信息和關系信息來處理。
通過整合相關的生成規則,我們可以增加對應的ABP框架代碼的生成,如下代碼生成工具界面所示。
最終根據根據選擇數據庫表信息,一鍵生成相關ABP架構分層代碼,文件結構如下所示。
對比前面項目的介紹,我們可以看到各個分層的類代碼是完全一致的。如對于領域層,包含了表名稱標記、字段信息和引用外鍵的對象。
/// <summary>
/// 通用字典明細項目信息,領域對象
/// </summary>
[Table("TB_DictData")]
public class DictData : FullAuditedEntity<string>
{
/// <summary>
/// 默認構造函數(需要初始化屬性的在此處理)
/// </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>
/// 通用字典明細項目信息,DTO對象
/// </summary>
public class DictDataDto
{
/// <summary>
/// 默認構造函數(需要初始化屬性的在此處理)
/// </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>
/// 創建通用字典明細項目信息,DTO對象
/// </summary>
public class CreateDictDataDto : DictDataDto
{
}
/// <summary>
/// 用于根據條件分頁查詢,DTO對象
/// </summary>
public class DictDataPagedDto : PagedResultRequestDto
{
public DictDataPagedDto() { }
/// <summary>
/// 參數化構造函數
/// </summary>
/// <param name="skipCount">跳過的數量</param>
/// <param name="resultCount">最大結果集數量</param>
public DictDataPagedDto(int skipCount, int resultCount)
{
this.SkipCount = skipCount;
this.MaxResultCount = resultCount;
}
/// <summary>
/// 使用分頁信息進行初始化SkipCount 和 MaxResultCount
/// </summary>
/// <param name="pagerInfo">分頁信息</param>
public DictDataPagedDto(PagerInfo pagerInfo)
{
if (pagerInfo != null)
{
//默認設置
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>
/// 通用字典明細項目信息,映射文件
/// </summary>
public class DictDataMapProfile : Profile
{
public DictDataMapProfile()
{
CreateMap<DictDataDto, DictData>();
CreateMap<DictData, DictDataDto>();
CreateMap<CreateDictDataDto, DictData>();
}
}
應用服務層接口實現代碼如下所示。
/// <summary>
/// 通用字典明細項目信息,應用層服務接口實現
/// </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分層的代碼實現如下所示。
/// <summary>
/// 通用字典明細項目信息的Web API調用處理
/// </summary>
public class DictDataApiCaller : AsyncCrudApiCaller<DictDataDto, string, DictDataPagedDto, CreateDictDataDto, DictDataDto>, IDictDataAppService
{
/// <summary>
/// 提供單件對象使用
/// </summary>
public static DictDataApiCaller Instance
{
get
{
return Singleton<DictDataApiCaller>.Instance;
}
}
/// <summary>
/// 默認構造函數
/// </summary>
public DictDataApiCaller()
{
this.DomainName = "DictData";//指定域對象名稱,用于組裝接口地址
}
}
這些信息是根據數據庫對應字段信息和關系信息進行批量生成,我們可以在這基礎上進行一定的調整,以及增加自己的業務接口,那么就非常方便了。
利用代碼生成工具的數據庫元數據,結合模板引擎NVelocity,我們可以為我們的項目框架代碼快速生成提供了一個快速有效、統一標準的生成方式,大大提高了生產效率。