在我們開發業務的時候,一般資料庫表都有相關的關系,除了單獨表外,一般還包括一對多、多對多等常見的關系,在實際開發程序中,需要結合系統框架做對應的處理,本篇隨筆介紹基于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代碼生成,如下界面所示,

然后在彈出的界面里面選擇主從表界面的生成選項卡即可,

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/209517.html
標籤:.NET Core
