在前面随笔,我介绍了整个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. 系统重装/装Anaconda后,Windows开始菜单缺少快捷方式解决方案

    系统重装后,想把D盘的软件添加快捷方式 以下以anaconda3为例,提供两种方法 方法一: 参考:  装Anaconda后,Windows开始菜单缺少快捷方式解决方案 方法二: 1. 添加环境变量 ...

  2. Java之Lambda表达式

    函数式编程思想概述 面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式做. 面向对象的思想: 做一件事情,找一个能解决这个事情的对 ...

  3. PalletOne调色板跨链的ETH提币实现

    实现区块链的跨链,最主要的诉求就是Token的转移,而Token的跨链转移又分为充币和提币2种操作.以PalletOne调色板来说,如果要把ETH跨链到PalletOne上来流转,就是ETH的充币操作 ...

  4. 在 Linux 下学习 C 语言有什么好处?

    作者:宅学部落链接:https://www.zhihu.com/question/23893390/answer/832610610来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请 ...

  5. 【SDOI 2015】约数个数和

    Problem Description 设 \(d(x)\) 为 \(x\) 的约数个数,给定 \(N\).\(M\),求 \[ \sum_{i=1}^N \sum_{j=1}^M d(ij) \] ...

  6. Selenium(十七):unittest单元测试框架(三) 脚本分析、编写Web用例

    1. 带unittest的脚本分析 也许你现在心里还有疑问,unittest框架与我们前面所编写的Web自动化测试之间有什么必然联系吗?当然有,既然unittest可以组织.运行测试用例,那么为什么不 ...

  7. Java每日一面(Part1:计算机网络)[19/10/21]

    作者:故事我忘了¢个人微信公众号:程序猿的月光宝盒 1.UDP简介 1.1UDP报文结构: ​ Source Port:源端口 Destination Port:目标端口 Length:数据包长度 C ...

  8. Java日期处理组件joda-time

    版权声明:本文为xing_star原创文章,转载请注明出处! 本文同步自http://javaexception.com/archives/175 Java日期处理组件joda-time 平常在开发过 ...

  9. Data Management Technology(3) -- SQL

    SQL is a very-high-level language, in which the programmer is able to avoid specifying a lot of data ...

  10. Linux ssh突然连接不了的案例浅析

    公司的Linux服务器都是通过一台JumpServer跳转的.个人使用Jumpserver(开源跳板机系统)时,有时候由于需要上传.下载文件很不方便.而由于配置关系,一般情况无法使用SecureCRT ...