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
中使用了。如果有改进的想法的可以自行实现或者留言讨论。