Abp 代碼生成器使用說明

Abp 代碼生成器使用說明

介紹

在使用abp框架進行項目開發的時候,開始使用的是52abp的代碼生成器,但是在使用的過程中,總有一些地方感覺不舒服,也許是出於程序員的強迫症吧。最終,我決定自己寫一個簡單的代碼生成器使用。所以,也就有了這麼一篇文章。

概述

我們先看看界面
在這裏插入圖片描述

只有這一個界面,因爲只是爲了生成後臺的代碼。從圖中可以看出,我只是生成了領域服務層EntityFrameworkCore層應用層的相關代碼,權限部分可以選擇生成,也可用後期用到在生成。這裏需要編輯的內容有:

1、實體類主鍵的類型,如Guidlong等;

2、實體類的中文名稱,可在生成類的註釋中使用,否則使用類名稱;

3、選擇在EditListDTO中顯示的字段,勾選則生成此字段;

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框架的相關說明信息

此示例代碼中添加RequiredStringLength特性,主要是爲了測試界面數據的綁定,其實此處可用不定義,而是放在生成的配置類中定義。

但是對於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("是否激活");
    }
}

其實個人更加的建議,將RequiredStringLength等特性的定義放在此處,同樣可在此處定義很多其他的約束,比如默認值等。

注意:生成的代碼中的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中使用了。如果有改進的想法的可以自行實現或者留言討論。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章