Abp 代碼生成器使用說明
介紹
在使用abp框架進行項目開發的時候,開始使用的是52abp的代碼生成器,但是在使用的過程中,總有一些地方感覺不舒服,也許是出於程序員的強迫症吧。最終,我決定自己寫一個簡單的代碼生成器使用。所以,也就有了這麼一篇文章。
概述
我們先看看界面
只有這一個界面,因爲只是爲了生成後臺的代碼。從圖中可以看出,我只是生成了領域服務層,EntityFrameworkCore層和應用層的相關代碼,權限部分可以選擇生成,也可用後期用到在生成。這裏需要編輯的內容有:
1、實體類主鍵的類型,如Guid
,long
等;
2、實體類的中文名稱,可在生成類的註釋中使用,否則使用類名稱;
3、選擇在Edit和List的DTO中顯示的字段,勾選則生成此字段;
4、字段中文名稱,實體類中默認有的會帶出來,必填也是如此;
5、如果實體類中有定義字段長度,此處會自動帶出,可在此修改,爲 0
則不會在DTO中生成。
具體使用方式
創建一個實體類
我們以BaseCode爲例子來說明。
首先,我們在領域層新建一個BaseCode文件夾,然後在此文件夾下創建BaseCode.cs類。在此類中添加相應的屬性信息,具體代碼如下:
public class BaseCode :Entity<Guid>,IPassivable
{
[Required]
[StringLength(maximumLength:50, ErrorMessage = "編碼不能超過50字符")]
[DisplayName("編碼")]
public string Code { get; set; }
[Required]
[StringLength(50,MinimumLength = 10,ErrorMessage ="編碼名稱不能超過50字符")]
[DisplayName("編碼名稱")]
public string CodeName { get; set; }
[StringLength(200)]
[DisplayName("描述")]
public string Desc { get; set; }
[Required]
[DisplayName("是否激活")]
public bool IsActive { get ; set; }
}
關於實體類的具體定義規則,可參考abp框架的相關說明信息。
此示例代碼中添加Required和StringLength特性,主要是爲了測試界面數據的綁定,其實此處可用不定義,而是放在生成的配置類中定義。
但是對於DisplayName特性,我建議您定義,因爲它不僅僅是在界面上綁定顯示,可用在DTO
中生成相應的註釋;同樣的,它還會生成相應的數據表字段說明。所以此處強烈建議您給出此字段的說明。
EF層生成的配置類
讓我們來看看生成的代碼:
/// <summary>
/// 基礎編碼 EF配置,可完成必填,註釋等內容的處理
/// </summary>
public class BaseCodeCfg : IEntityTypeConfiguration<BaseCodeBaseCode>
{
/// <summary>
/// 具體配置方法
/// </summary>
/// <param name="builder"></param>
public void Configure(EntityTypeBuilder<BaseCodeBaseCode>builder)
{
// ABP 爲數據庫中架構名稱,可自行修改,sql server 的默認架構是dbo
builder.ToTable("BaseCode", "ABP").HasComment("基礎編碼的相關信息") ;
builder.Property(x => x.Id).HasComment(AbpConsts.CommentPrimaryKeyGuid);
builder.Property(x => x.Code).HasComment("編碼");
builder.Property(x => x.CodeName).HasComment("編碼名稱");
builder.Property(x => x.Desc).HasComment("描述");
builder.Property(x => x.IsActive).HasComment("是否激活");
}
}
其實個人更加的建議,將Required和StringLength等特性的定義放在此處,同樣可在此處定義很多其他的約束,比如默認值等。
注意:生成的代碼中的AbpConsts類是在領域層中生成的。
生成的項目結構如圖:
爲了使用生成配置類,我們需要在xxDbContext類中添加相關內容,此類可用在EF
層的EntityFrameworkCore文件夾下找到。
添加的內容主要是兩塊,一個是實體類數據集屬性,另一個是重寫OnModelCreating方法。代碼分別如下:
/* Define a DbSet for each entity of the application */
public DbSet<BaseCode.BaseCode> BaseCodes;
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration<BaseCode.BaseCode>(new BaseCodeCfg());
base.OnModelCreating(modelBuilder);
}
這樣在生成數據庫時,相應的表字段註釋也會隨之生成。具體生成方式,可參考代碼生成器中生成的readme.md
說明文檔。
讓我們看下生成的數據庫效果:
領域層(XX.Core)生成的類
在創建好的實體類上右擊,在彈出的菜單中選擇【Abp 代碼生成器】後即可看到本文開頭展示的界面。生成此層的相關代碼,必須勾選【領域服務】複選框。
生成後,會在實體類的同層創建一個DomainService文件夾,並在其下生成領域服務接口和領域服務實現類。在領域層的根目錄下生成一個AbpConsts.cs類和XXXDomainServiceBase.cs類。最後要說的是,還會在此層的根目錄下生成一個Readme.md
的說明文檔。
這裏簡單展示下XXXDomainServiceBase.cs類的代碼:
/// <summary>
/// 自定義的基礎領域服務,繼承自abp框架的領域服務
/// </summary>
public abstract class DemoDomainServiceBase : DomainService
{
/// <summary>
/// 構造函數
/// </summary>
protected DemoDomainServiceBase()
{
LocalizationSourceName = DemoConsts.LocalizationSourceName;
}
/* Add your common members for all your domain services. */
/*在領域服務中添加你的自定義公共方法*/
}
項目結構可參考權限部分給的圖片
應用層(XX.Application)生成的類
相對來說,這一層中生成的類最多。
首先我們來看看在應用層根目錄下生成的XXXCrudAppServiceBase.cs類。其生成的代碼如下:
/// <summary>
/// 應用層增刪改查的基類,自定義的業務類都可從此類繼承
/// </summary>
public abstract class DemoCrudAppServiceBase<TEntity, TEntityDto, TPrimaryKey, TGetAllInput, TCreateInput, TUpdateInput> : AsyncCrudAppService<TEntity, TEntityDto, TPrimaryKey, TGetAllInput, TCreateInput, TUpdateInput, EntityDto<TPrimaryKey>>
where TEntity : class, IEntity<TPrimaryKey>
where TEntityDto : IEntityDto<TPrimaryKey>
where TUpdateInput : IEntityDto<TPrimaryKey>
{
/// <summary>
/// 租戶管理
/// </summary>
public TenantManager TenantManager { get; set; }
/// <summary>
/// 用戶管理
/// </summary>
public UserManager UserManager { get; set; }
///<summary>
/// 構造函數
/// </summary>
public DemoCrudAppServiceBase(IRepository<TEntity, TPrimaryKey> repository) : base(repository)
{
LocalizationSourceName = DemoConsts.LocalizationSourceName;
}
/// <summary>
/// 得到當前用戶信息
/// </summary>
/// <returns></returns>
protected virtual async Task<User> GetCurrentUserAsync()
{
var user = await UserManager.FindByIdAsync(AbpSession.GetUserId().ToString());
if (user == null)
{
throw new Exception("There is no current user!");
}
return user;
}
/// <summary>
/// 得到當前租戶信息
/// </summary>
/// <returns></returns>
protected virtual Task<Tenant> GetCurrentTenantAsync()
{
return TenantManager.GetByIdAsync(AbpSession.GetTenantId());
}
/// <summary>
/// 檢查錯誤
/// </summary>
/// <param name="identityResult"></param>
protected virtual void CheckErrors(IdentityResult identityResult)
{
identityResult.CheckErrors(LocalizationManager);
}
}
因爲應用層的代碼我主要是參考Abp
框架的源碼來處理的,而 AsyncCrudAppService<TEntity, TEntityDto, TPrimaryKey, TGetAllInput, TCreateInput, TUpdateInput, EntityDto<TPrimaryKey>>
是在其源碼中定義好的,可以直接使用的,所以纔有了這個公共父類的實現。具體可查看Abp框架的源碼。
其次,在應用層的根目錄下創建一個Dtos文件夾,其下放了三個處理分頁相關功能的類。
最主要的是應用服務相關的類,其文件結構和實體類建立時的文件結構相同,具體可參見下圖:
如圖,代碼分三塊:領域服務接口和其實現類,AutoMapper的映射配置類,還有就是具體的Dto類。這裏的Dto類只是最基本的添加、編輯和列表及分頁的類,可根據業務情況自行添加新的Dto類。
1、AutoMapper配置類的代碼如下:
/// <summary>
/// automapper配置類
/// </summary>
public class BaseCodeMapProfile : Profile
{
/// <summary>
/// 構造函數
/// </summary>
public BaseCodeMapProfile()
{
CreateMap<BaseCode.BaseCode, BaseCodeListDto>();
CreateMap<BaseCodeListDto, BaseCode.BaseCode>();
CreateMap<BaseCode.BaseCode, CreateBaseCodeInputDto>();
CreateMap<CreateBaseCodeInputDto, BaseCode.BaseCode>();
CreateMap<BaseCodeEditDto, BaseCode.BaseCode>();
CreateMap<BaseCode.BaseCode,BaseCodeEditDto>();
}
}
定義的配置類直接繼承了Profile類,這樣只需要維護這個類中的映射關係,而不需要做其他的改動即可。這是因爲在XXXApplicationModule.cs中定義瞭如下內容,自動完成了配置類的注入。
public override void Initialize()
{
var thisAssembly = typeof(DemoApplicationModule).GetAssembly();
IocManager.RegisterAssemblyByConvention(thisAssembly);
Configuration.Modules.AbpAutoMapper().Configurators.Add(
// Scan the assembly for classes which inherit from AutoMapper.Profile
cfg => cfg.AddMaps(thisAssembly)
);
}
2、關於Dtos中的類,我們以XXXEditDto類爲例看看,代碼如下:
/// <summary>
/// BaseCode.BaseCode 的編輯DTO
/// <see cref="BaseCode.BaseCode"/>
/// </summary>
public class BaseCodeEditDto : EntityDto<Guid>
{
/// <summary>
/// 編碼
/// </summary>
[Required]
[StringLength(50,ErrorMessage = "編碼最大長度爲50,最小長度爲5",MinimumLength = 5)]
public string Code { get; set; }
/// <summary>
/// 編碼名稱
/// </summary>
[Required]
[StringLength(50,ErrorMessage = "編碼名稱最大長度爲50,最小長度爲5",MinimumLength = 5)]
public string BaseName { get; set; }
/// <summary>
/// 備註
/// </summary>
[StringLength(200,ErrorMessage = "備註最大長度200")]
public string Remark { get; set; }
/// <summary>
/// 是否激活
/// </summary>
[Required]
public bool IsActive { get; set; }
}
從此代碼中可以看出,配置界面中的關於EditDto的相關配置內容都在此有所體現。
3、而對於應用服務類的生成,其接口類如下:
/// <summary>
/// 應用層服務的接口方法
///</summary>
public interface IBaseCodeAppService : IAsyncCrudAppService<BaseCodeListDto, Guid,PagedBaseCodeResultRequestDto, CreateBaseCodeInputDto, BaseCodeEditDto>
{
}
可以看到,其繼承了Abp框架
中定義的IAsyncCrudAppService接口,此接口中定義好了關於增刪改查的相關方法,可以直接重寫即可。生成的實現類代碼如下:
/// <summary>
/// 應用層服務的接口實現方法
///</summary>
[AbpAuthorize]
public class BaseCodeAppService : DemoCrudAppServiceBase<BaseCode.BaseCode, BaseCodeListDto,Guid, PagedBaseCodeResultRequestDto, CreateBaseCodeInputDto, BaseCodeEditDto>,IBaseCodeAppService
{
private readonly IBaseCodeManager _baseCodeManager;
/// <summary>
/// 構造函數
///</summary>
public BaseCodeAppService(IRepository<BaseCode.BaseCode, Guid> baseCodeRepository, IBaseCodeManager baseCodeManager) : base(baseCodeRepository)
{
_baseCodeManager = baseCodeManager;
}
/// <summary>
/// 添加基礎編碼
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public override async Task<BaseCodeListDto> CreateAsync(CreateBaseCodeInputDto input)
{
var entity = ObjectMapper.Map<BaseCode.BaseCode>(input);
await _baseCodeManager.CreateAsync(entity);
return MapToEntityDto(entity);
}
/// <summary>
/// 修改基礎編碼
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public override async Task<BaseCodeListDto> UpdateAsync(BaseCodeEditDto input)
{
var entity = await this._baseCodeManager.FindByIdAsync(input.Id);
ObjectMapper.Map(input, entity);
return MapToEntityDto(entity);
}
/// <summary>
/// 刪除基礎編碼
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public override async Task DeleteAsync(EntityDto<Guid> input)
{
await this._baseCodeManager.DeleteAsync(input.Id);
}
/// <summary>
/// 得到所有的基礎編碼
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public override Task<PagedResultDto<BaseCodeListDto>> GetAllAsync(PagedBaseCodeResultRequestDto input)
{
return base.GetAllAsync(input);
}
/// <summary>
/// 根據id得到指定的病區信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public override Task<BaseCodeListDto> GetAsync(EntityDto<Guid> input)
{
return base.GetAsync(input);
}
/// <summary>
/// 創建過濾條件的自定義,在獲取列表信息中使用
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
protected override IQueryable<BaseCode.BaseCode> CreateFilteredQuery(PagedBaseCodeResultRequestDto input)
{
return Repository.GetAll().WhereIf(!input.Keyword.IsNullOrWhiteSpace(), x =>
x.Code.Contains(input.Keyword) ||
x.BaseName.Contains(input.Keyword) ||
x.Remark.Contains(input.Keyword));
}
/// <summary>
/// 重寫排序規則
/// </summary>
/// <param name="query"></param>
/// <param name="input"></param>
/// <returns></returns>
protected override IQueryable<BaseCode.BaseCode> ApplyPaging(IQueryable<BaseCode.BaseCode> query, PagedBaseCodeResultRequestDto input)
{
return base.ApplyPaging(query, input);
}
}
權限相關的類
權限相關的類也是在領域層生成的,在實體類同層創建一個Authorization文件夾,和領域服務的文件夾也是同層的。在此文件夾下有兩個類,分別是XXXAuthorizationProvider.cs類和XXXPermissions.cs類。
還有一個類放在領域層根目錄的Authorization文件夾下,這個文件夾及下面的相關類是Abp框架
生成的,我們只是在下面添加一個AbpPermissions.cs類。
生成的項目結構如下:
這裏簡單展示下AbpPermissions.cs類的代碼:
/// <summary>
/// 項目定義的默認權限變量名稱
/// </summary>
public class AbpPermissions
{
public const string Pages = "Pages";
public const string Pages_Administration = "Pages.Administration";
// 可自定義新的權限
}
總結
此代碼生成器只是實現了最基本的代碼實現,對於前端和單元測試等都沒有實現。對於一些細節,如“是否第一次生成”這樣的,也沒有進行處理。個人考慮是,因爲代碼中對於文件夾、文件的生成都有判斷,有則不在生成,沒有才會生成。在不進行模板翻譯而只是判斷有無的情況下,速度很快,所以沒有考慮進行界面的配置。
對於項目名稱結構,也只是測試了形如xxx.xxx.Core的項目,對於xxx.Core形式的命名項目沒有進行測試。關於實體類層級,我只測試了一層和兩層的情況,即folder/entity.cs的形式和folder1/folder2/entity.cs的形式。
我已將源代碼放在github上,可自行獲取代碼進行查看。可在bin/debug文件夾下AbpGenerateProject.vsix
文件並點擊安裝,安裝好後就可以在vs2019
中使用了。如果有改進的想法的可以自行實現或者留言討論。