ABP框架中一对多,多对多关系的处理以及功能界面的处理(1)
在我们开发业务的时候,一般数据库表都有相关的关系,除了单独表外,一般还包括一对多、多对多等常见的关系,在实际开发过程中,需要结合系统框架做对应的处理,本篇随笔介绍基于ABP框架对EF实体、DTO关系的处理,以及提供对应的接口进行相关的数据保存更新操作。
1、一对多关系的数据处理
一对多,也可以叫做主从表的关系,其中从表有一个外键和主表进行关联,如下所示。
上图是一个简单的主从表关系,其中客户信息表只有简单的一两个字段用于演示,从表用来记录对应客户的地址信息。
其中表中的CreateUserId、CreateTime、LastModifierUserId、LastModificationTime、DeleterUserId、IsDeleted、DeletionTime、TenantId字段,是我们一般设计ABP表保留的字段。
我们先从一个关系图来了解下框架下的领域驱动模块中的各个类之间的关系。
ABP框架,和应用服务层或者界面层打交道的数据对象是DTO对象,和数据库打交道的是实体对象,连接连接起来是通过AutoMapper实现映射处理,映射是通过映射文件进行配置,一般我们可以根据数据库表信息进行生成DTO、Entity,以及映射文件。
以客户及客户地址表为例,生成的DTO对象如下所示。
/// <summary>
/// 创建客户信息,DTO对象
/// </summary>
public class CreateCustomerDto : FullAuditedEntityDto<string>
{
/// <summary>
/// 默认构造函数(需要初始化属性的在此处理)
/// </summary>
public CreateCustomerDto()
{
this.Id = Guid.NewGuid().ToString();
} #region Property Members /// <summary>
/// 姓名
/// </summary>
[Required]
public virtual string Name { get; set; } /// <summary>
/// 年龄
/// </summary>
//[Required]
public virtual int? Age { get; set; } #endregion } /// <summary>
/// 客户信息,DTO对象
/// </summary>
public class CustomerDto : CreateCustomerDto
{
}
/// <summary>
/// 创建客户地址簿,DTO对象
/// </summary>
public class CreateCustomerAddressDto : CreationAuditedEntityDto<string>
{
/// <summary>
/// 默认构造函数(需要初始化属性的在此处理)
/// </summary>
public CreateCustomerAddressDto()
{
this.Id = System.Guid.NewGuid().ToString();
} #region Property Members /// <summary>
/// 客户ID
/// </summary>
//[Required]
public virtual string Customer_ID { get; set; } /// <summary>
/// 省份
/// </summary>
//[Required]
public virtual string Province { get; set; } /// <summary>
/// 城市
/// </summary>
//[Required]
public virtual string City { get; set; } /// <summary>
/// 区县
/// </summary>
//[Required]
public virtual string District { get; set; } /// <summary>
/// 详细地址
/// </summary>
//[Required]
public virtual string DetailAddress { get; set; } /// <summary>
/// 排序
/// </summary>
//[Required]
public virtual string SortCode { get; set; } #endregion } /// <summary>
/// 客户地址簿,DTO对象
/// </summary>
public class CustomerAddressDto : CreateCustomerAddressDto
{
}
其表对应的实体类,也和DTO类似,不过是和数据库打交道的数据对象
/// <summary>
/// 客户信息,领域对象
/// </summary>
[Table("T_Customer")]
public class Customer : FullAuditedEntity<string>
{
/// <summary>
/// 默认构造函数(需要初始化属性的在此处理)
/// </summary>
public Customer()
{
} #region Property Members /// <summary>
/// 姓名
/// </summary>
//[Required]
public virtual string Name { get; set; } /// <summary>
/// 年龄
/// </summary>
//[Required]
public virtual int? Age { get; set; } #endregion }
/// <summary>
/// 客户地址簿,领域对象
/// </summary>
[Table("T_CustomerAddress")]
public class CustomerAddress : CreationAuditedEntity<string>
{
/// <summary>
/// 默认构造函数(需要初始化属性的在此处理)
/// </summary>
public CustomerAddress()
{
} #region Property Members /// <summary>
/// 客户ID
/// </summary>
//[Required]
public virtual string Customer_ID { get; set; } /// <summary>
/// 省份
/// </summary>
//[Required]
public virtual string Province { get; set; } /// <summary>
/// 城市
/// </summary>
//[Required]
public virtual string City { get; set; } /// <summary>
/// 区县
/// </summary>
//[Required]
public virtual string District { get; set; } /// <summary>
/// 详细地址
/// </summary>
//[Required]
public virtual string DetailAddress { get; set; } /// <summary>
/// 排序
/// </summary>
//[Required]
public virtual string SortCode { get; set; } /// <summary>
/// 客户ID
/// </summary>
//[Required]
[ForeignKey("Customer_ID")]
public virtual Customer Customer { get; set; } #endregion }
映射文件如下所示。
/// <summary>
/// 客户信息,映射文件
/// </summary>
public class CustomerMapProfile : Profile
{
public CustomerMapProfile()
{
CreateMap<CustomerDto, Customer>();
CreateMap<Customer, CustomerDto>();
CreateMap<CreateCustomerDto, Customer>();
}
}
/// <summary>
/// 客户地址簿,映射文件
/// </summary>
public class CustomerAddressMapProfile : Profile
{
public CustomerAddressMapProfile()
{
CreateMap<CustomerAddressDto, CustomerAddress>();
CreateMap<CustomerAddress, CustomerAddressDto>();
CreateMap<CreateCustomerAddressDto, CustomerAddress>();
}
}
然后在EFCore的上下文中添加对应的DBSet对象即可。
有了这些,基于ABP框架的基础上就可以实现数据的创建、更新提交了。
2、一对多关系的界面处理和服务端ABP接口的处理
但是主从表之间的关系,我们这里还没有详细说明,一般我们在界面处理数据的时候,主表数据可能和从表数据一起显示,编辑的时候一起保存,如下界面所示。
在编辑/新增界面中绑定GridView的相关显示和处理事件。
我们可以在新增窗口中加载空地址列表,或者编辑窗口加载已有地址列表记录
保存新增的记录如下所示。
/// <summary>
/// 新增状态下的数据保存
/// </summary>
/// <returns></returns>
public async override Task<bool> SaveAddNew()
{
CustomerDto info = tempInfo;//必须使用存在的局部变量,因为部分信息可能被附件使用
SetInfo(info); try
{
#region 新增数据 tempInfo = await CustomerApiCaller.Instance.CreateAsync(info);
if (tempInfo != null)
{
//可添加其他关联操作
var list = GetDetailList();
foreach(var detailInfo in list)
{
await CustomerAddressApiCaller.Instance.InsertOrUpdateAsync(detailInfo);
}
return true;
}
#endregion
}
catch (Exception ex)
{
LogTextHelper.Error(ex);
MessageDxUtil.ShowError(ex.Message);
}
return false;
}
其中GetDetailList是获取编辑状态下的数据记录
/// <summary>
/// 获取明细列表
/// </summary>
/// <returns></returns>
private List<CustomerAddressDto> GetDetailList()
{
var list = new List<CustomerAddressDto>();
for (int i = 0; i < this.gridView1.RowCount; i++)
{
var detailInfo = gridView1.GetRow(i) as CustomerAddressDto;
if (detailInfo != null)
{
list.Add(detailInfo);
}
}
return list;
}
如果数据更新的时候,操作也是类似
/// <summary>
/// 编辑状态下的数据保存
/// </summary>
/// <returns></returns>
public override async Task<bool> SaveUpdated()
{
CustomerDto info = await CustomerApiCaller.Instance.GetAsync(ID);
if (info != null)
{
SetInfo(info); try
{
#region 更新数据
tempInfo = await CustomerApiCaller.Instance.UpdateAsync(info);
if (tempInfo != null)
{
//可添加其他关联操作
var list = GetDetailList();
foreach(var detailInfo in list)
{
await CustomerAddressApiCaller.Instance.InsertOrUpdateAsync(detailInfo);
}
return true;
}
#endregion
}
catch (Exception ex)
{
LogTextHelper.Error(ex);
MessageDxUtil.ShowError(ex.Message);
}
}
return false;
}
我们这里注意到不管更新还是插入地址记录,都用到了一个函数InsertOrUpdateAsync,这个是我们后台判断记录是新增或者更新,在写入数据库操作中的处理函数。
这个函数比较通用,我们可以考虑把它写入公用的基类接口里面即可。
同样,客户端的ApiCaller调用,也需要进行一个简单的基类接口增加即可。
有了这些支持,Winform客户端的处理就可以直接调用这些简单的接口进行主从表的数据提交了。
//可添加其他关联操作
var list = GetDetailList();
foreach(var detailInfo in list)
{
await CustomerAddressApiCaller.Instance.InsertOrUpdateAsync(detailInfo);
}
另外,除了这种细粒度的接口处理,我们还可以把整个DTO对象包装一下,在主表DTO对象中包含从表明细列表,然后重写Create、Update的服务端应用服务层接口,接收从表明细,然后一个接口就可以处理主从表的数据保存或者更新了。
具体如何选择数据处理的方式,需要根据业务的场景进行衡量。
3、结合代码生成工具实现后台代码和主从表界面代码的快速生成
一旦业务规则确定,我们可以运用代码生成工具来提高开发效率了。由于主从表关系的处理比较统一,因此我们可以按照他们的关系以及界面常见的处理方式来生成这些内容。
首先,我们打开代码生成工具,展开对应数据库的表信息,如下界面所示。
选择ABP框架代码生成,可以生成后台框架代码,其中包括DTO实体、实体对象、映射文件,服务端应用层接口和实现等内容。
生成Winform主从表界面的时候,选择Winform代码生成,如下界面所示。
然后在弹出的界面里面选择主从表界面的生成选项卡即可。
ABP框架中一对多,多对多关系的处理以及功能界面的处理(1)的更多相关文章
- hibernate中一对多多对一关系设计的理解
1.单向多对一和双向多对一的区别? 只需要从一方获取另一方的数据时 就使用单向关联双方都需要获取对方数据时 就使用双向关系 部门--人员 使用人员时如果只需要获取对应部门信息(user.getdept ...
- SSAS中事实表中的数据如果因为一对多或多对多关系复制了多份,在维度上聚合的时候还是只算一份
SSAS事实表中的数据,有时候会因为一对多或多对多关系发生复制变成多份,如下图所示: 图1 我们可以从上面图片中看到,在这个例子中,有三个事实表Fact_People_Money(此表用字段Money ...
- JPA实体关系映射:@ManyToMany多对多关系、@OneToMany@ManyToOne一对多多对一关系和@OneToOne的深度实例解析
JPA实体关系映射:@ManyToMany多对多关系.@OneToMany@ManyToOne一对多多对一关系和@OneToOne的深度实例解析 今天程序中遇到的错误一 org.hibernate.A ...
- AutoMapper在ABP框架中的使用说明
为了说明AutoMapper如何使用,我专门开设了一个专题来讲,如果您还没有查看该专题,请点击这里.既然系统地学习了AutoMapper,那么接下来就是该用它实战的时候了.今天,我们就来揭开AutoM ...
- SSAS中CUBE的多对多关系既可以出现在中间事实表上也可以出现在中间维度表上
开发过SSAS中CUBE的朋友,肯定都知道维度用法中的多对多关系, 这篇文章不想详细阐述多对多关系在CUBE中的结构,详情请在网上寻找CUBE多对多关系的介绍资料. 下面是是一个典型的CUBE中多对多 ...
- 2018.11.4 Hibernate中一对、多对多的关系
简单总结一下 多表关系 一对多/多对一 O 对象 一的一方使用集合. 多的一方直接引用一的一方. R 关系型数据库 多的一方使用外键引用一的一方主键. M 映射文件 一: 多: 操作: 操作管理级别属 ...
- Hibernate框架学习(六)——一对多&多对一关系
一.关系表达 1.表中的表达 2.实体中的表达 3.orm元数据中的表达 一对多:(在Customer.hbm.xml中添加) 多对一:(在LinkMan.hbm.xml中添加) 最后别忘了在hibe ...
- SQLAlchemy_定义(一对一/一对多/多对多)关系
目录 Basic Relationship Patterns One To Many One To One Many To Many Basic Relationship Patterns 基本关系模 ...
- hibernate中一对多 多对多 inverse cascade
----------------------------一对多------------------------------------------- inverse属性:是在维护关联关系的时候起作用的 ...
随机推荐
- Oracle Database XE 11gR2 自带的用户,新建用户,修改用户密码
操作系统:Windows 10 x64 第一节:下载 Oracle Database XE 11gR2 第二节:安装.验证安装 Oracle Database XE 11gR2 第三节:Oracle ...
- Arduino Mega 2560
Arduino Mega 2560 www.theengineeringprojects.com/ 此板子有54个引脚,16个模拟量输入引脚,12个PWM输出引脚,4个串口,带I2C,SPI通讯口,更 ...
- 《穷查理年鉴》贪嗔痴 & 懒贪装(关于败坏)
贪嗔痴 & 懒贪装 1)伤害 041.仇恨加重伤害,漠视消除伤害. 042.90%的伤害是自己造成的. 044.伤害你的敌人使你比他更低下;仇恨使你和他一样;宽恕才能让你超越他. 109.让仇 ...
- CentOS7 下 swap 分区的创建、删除及相关配置
一般我们在购买云服务器(例如:阿里云ECS.腾讯云服务器)的时候,选择 CentOS 7 系统之后,登录系统,发现 swap 大小为 0(即没有分配). 如果我们想在该 服务器上安装 Oracle 数 ...
- 发布MeteoInfo 1.2.5
提升了MeteoInfoLab脚本数据处理能力,比如双Y轴图.多Y轴图.数组计算.坐标投影计算等.这里给出几个示例图,以后有空了会将示例脚本程序整理放在网上.坐标投影计算: 双Y轴图: 多Y轴图: 多 ...
- ps 树形显示
ps -axjf|grep swoft
- spring boot:使用mybatis访问多个mysql数据源/查看Hikari连接池的统计信息(spring boot 2.3.1)
一,为什么要访问多个mysql数据源? 实际的生产环境中,我们的数据并不会总放在一个数据库, 例如:业务数据库:存放了用户/商品/订单 统计数据库:按年.月.日的针对用户.商品.订单的统计表 因为统计 ...
- linux(centos8):为prometheus安装grafana(grafana-7.0.3)
一,grafana的用途 1,grafana是什么? grafana 是用 go 语言编写的开源应用, 它的主要用途是大规模指标数据的可视化展现 它是现在网络架构/应用分析中最流行的时序数据展示工具 ...
- centos8环境安装配置rsync
一,查看本地centos的版本: [root@localhost lib]# cat /etc/redhat-release CentOS Linux release 8.1.1911 (Core) ...
- linux 压缩命令 zip
1.zip命令 例如:zip -r mysql.zip mysql 该句命令的含义是:将mysql文件夹压缩成mysql.zip zip -r abcdef.zip abc def.txt 这句命令的 ...