在前面随笔,我介绍了整个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 > ? pagerInfo.PageSize : ;
var pageIndex = pagerInfo.CurrenetPageIndex > ? pagerInfo.CurrenetPageIndex : ; this.SkipCount = pageSize * (pageIndex - );
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,我们可以为我们的项目框架代码快速生成提供了一个快速有效、统一标准的生成方式,大大提高了生产效率。

利用代码生成工具生成基于ABP框架的代码的更多相关文章

  1. 使用代码生成工具快速生成基于ABP框架的Vue+Element的前端界面

    世界上唯一不变的东西就是变化,我们通过总结变化的规律,以规律来应付变化,一切事情处理起来事半功倍.我们在开发后端服务代码,前端界面代码的时候,界面都是依照一定的规律进行变化的,我们通过抽取数据库信息, ...

  2. 使用代码生成工具快速开发ABP框架项目

    在一般系统开发中,我们一般要借助于高度定制化的代码生成工具,用于统一代码风,节省开发时间,提高开发效率.不同的项目,它的项目不同分层的基类定义不同,我们需要在框架基类的基础上扩展我们的业务类代码,尽量 ...

  3. 利用代码生成工具Database2Sharp生成ABP VNext框架项目代码

    我们在做某件事情的时候,一般需要详细了解它的特点,以及内在的逻辑关系,一旦我们详细了解了整个事物后,就可以通过一些辅助手段来提高我们的做事情的效率了.本篇随笔介绍ABP VNext框架各分层项目的规则 ...

  4. 利用代码生成工具Database2Sharp设计数据编辑界面

    在Winform程序开发中,界面部分的开发工作量一般是比较大的,特别是表的字段数据比较多的情况下,数据编辑界面所需要的繁琐设计和后台逻辑处理工作量更是直线上升,而且稍不注意,可能很多处理有重复或者错误 ...

  5. Web API应用架构在Winform混合框架中的应用(4)--利用代码生成工具快速开发整套应用

    前面几篇介绍了Web API的基础信息,以及如何基于混合框架的方式在WInform界面里面整合了Web API的接入方式,虽然我们看似调用过程比较复杂,但是基于整个框架的支持和考虑,我们提供了代码生成 ...

  6. 在基于ABP框架的前端项目Vue&Element项目中采用电子签名的处理

    在前面随笔介绍了<在基于ABP框架的前端项目Vue&Element项目中采用电子签章处理文件和打印处理>的处理,有的时候,我们在流程中或者一些文件签署的时候,需要签上自己的大名,一 ...

  7. 基于abp框架的数据库种子数据初始化

    目录 基于abp框架的数据库种子数据初始化 1.背景 2.参照 3.解决方案 3.1 初始化数据 3.2 依赖注入方法容器里获取数据库上下文 3.3 封装创建初始化数据列表方法 3.4 数据库中没有的 ...

  8. 根据wsdl,apache cxf的wsdl2java工具生成客户端、服务端代码

    根据wsdl,apache cxf的wsdl2java工具生成客户端.服务端代码 apache cxf的wsdl2java工具的简单使用: 使用步骤如下: 一.下载apache cxf的包,如apac ...

  9. 基于ABP框架的SignalR,使用Winform程序进行功能测试

    在ABP框架里面,默认会带入SignalR消息处理技术,它同时也是ABP框架里面实时消息处理.事件/通知处理的一个实现方式,SignalR消息处理本身就是一个实时很好的处理方案,我在之前在我的Winf ...

随机推荐

  1. 蓝牙spp协议分析

    基本概念 蓝牙串口是基于 SPP 协议(Serial Port Profile),能在蓝牙设备之间创建串口进行数据传输的一种设备. 蓝牙串口的目的是针对如何在两个不同设备(通信的两端)上的应用之间保证 ...

  2. 解决IDEA中的DashBoard 不显示端口号

    第一步:找到.idea目录下的workspace.xml文件 第二步:找到下图位置 并添加红色部分代码 添加代码如下: <option name="configurationTypes ...

  3. 【c&c++】变量初始化

    /* 全局变量会默认初始化为0 空 和 false,局部变量要手动初始化,或者直接scanf输入值进去.如果不初始化,n可能是0也有可能是其他数,大多数情况下会是0,但不初始化是不安全的,所以Dev直 ...

  4. mysql workbench 报错:Can't analyze file, please try to change encoding type...

    Mysql workbench 导入csv can't analyze file 原因: workbench 识别csv第一行作为column名,column名不能为中文,所以报错.解决方法:csv第 ...

  5. 【位图算法】什么是BitMap

    目录 1. 位图算法的简单原理 2. BitMap的开源实现 3. 使用案列 BitMap算法的核心思想是用bit数组来记录0-1两种状态,然后再将具体数据映射到这个比特数组的具体位置,这个比特位设置 ...

  6. 在IntelliJ IDEA中,Lombok注解@Slf4j找不到log解决方案

    在IntelliJ IDEA中,注解@Slf4j找不到log时,可以安装Lombok插件 File → settings → Plugins, 然后点击"Browse repositorie ...

  7. Java入门系列之集合ArrayList源码分析(七)

    前言 上一节我们通过排队类实现了类似ArrayList基本功能,当然还有很多欠缺考虑,只是为了我们学习集合而准备来着,本节我们来看看ArrayList源码中对于常用操作方法是如何进行的,请往下看. A ...

  8. [Spring cloud 一步步实现广告系统] 10. 使用Ribbon 实现微服务调用

    在使用Ribbon调用广告投放系统API之前,我们需要先创建2个VO对象,AdPlanVO,AdPlanGetRequestVO. //数据请求对象 @Data @NoArgsConstructor ...

  9. Change Field Layout and Visibility in a List View 在列表视图中更改字段布局和可见性

    This lesson will guide you through the steps needed to select columns displayed in the List View. Fo ...

  10. Specify Action Settings 指定按钮设置

    In this lesson, you will learn how to modify Action properties. The ClearTasks Action will be used. ...