在上一篇文章中學習了ABP的倉儲(Repository)功能,Repository對資料庫進行增刪改查操作。在這一篇文章中我們主要瞭解應用服務層。 應用服務用於將領域(業務)邏輯暴露給展現層。展現層通過傳入DTO(數據傳輸對象)參數來調用應用服務,而應用服務通過領域對象來執行相應的業務邏輯並且將DT... ...
abp(net core)+easyui+efcore倉儲系統目錄
abp(net core)+easyui+efcore倉儲系統——ABP總體介紹(一)
abp(net core)+easyui+efcore倉儲系統——解決方案介紹(二)
abp(net core)+easyui+efcore倉儲系統——領域層創建實體(三)
abp(net core)+easyui+efcore倉儲系統——定義倉儲並實現 (四)
在上一篇文章中學習了ABP的倉儲(Repository)功能,Repository對資料庫進行增刪改查操作。在這一篇文章中我們主要瞭解應用服務層。
一、解釋下應用服務層
應用服務用於將領域(業務)邏輯暴露給展現層。展現層通過傳入DTO(數據傳輸對象)參數來調用應用服務,而應用服務通過領域對象來執行相應的業務邏輯並且將DTO返回給展現層。因此,展現層和領域層將被完全隔離開來。
以下幾點,在創建應用服務時需要註意:
- 在ABP中,一個應用服務需要實現IApplicationService介面,最好的實踐是針對每個應用服務都創建相應繼承自IApplicationService的介面。(通過繼承該介面,ABP會自動幫助依賴註入)
- ABP為IApplicationService提供了預設的實現ApplicationService,該基類提供了方便的日誌記錄和本地化功能。實現應用服務的時候繼承自ApplicationService並實現定義的介面即可。
- ABP中,一個應用服務方法預設是一個工作單元(Unit of Work)。ABP針對UOW模式自動進行資料庫的連接及事務管理,且會自動保存數據修改。
二、定義應用服務介面需要用到的DTO
1. 在Visual Studio 2017的“解決方案資源管理器”中,右鍵單擊“ABP.TPLMS.Application”項目。 選擇“添加” > “新建文件夾”。
2.將文件夾命名為“Modules”。
3. 右鍵單擊“Modules”文件夾,選擇“添加” > “新建文件夾”。將文件夾重命名為“Dto”。如下圖。
4. 右鍵單擊“Dto”文件夾,然後選擇“添加” > “類”。 將類命名為 ModuleDto,然後選擇“添加”。代碼如下。
using Abp.Application.Services.Dto; using Abp.AutoMapper; using ABP.TPLMS.Entitys; using System; using System.Collections.Generic; using System.Text; namespace ABP.TPLMS.Modules.Dto { [AutoMapFrom(typeof(Module))] public class ModuleDto:EntityDto<long> { public string DisplayName { get; set; } public string Name { get; set; } public string Url { get; set; } public string HotKey { get; set; } public int ParentId { get; set; } public bool RequiresAuthentication { get; set; } public bool IsAutoExpand { get; set; } public string IconName { get; set; } public int Status { get; set; } public string ParentName { get; set; } public string RequiredPermissionName { get; set; } public int SortNo { get; set; } } }
- 為了在頁面上展示模塊信息,Module
Dto
被用來將模塊數據傳遞到基礎設施層。 - Module
Dto
繼承自EntityDto<long>
.跟在領域層定義的Module類一樣具有一些相同屬性。 [AutoMapFrom(typeof(Module))]
用來創建從Module類到ModuleDto
的映射.使用這種方法。你可以將Module對象自動轉換成ModuleDto
對象(而不是手動複製所有的屬性)。
5. 右鍵單擊“Dto”文件夾,然後選擇“添加” > “類”。 將類命名為 CreateUpdateModuleDto ,然後選擇“添加”。代碼如下。
using Abp.Application.Services.Dto; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Text; namespace ABP.TPLMS.Modules.Dto { public class CreateUpdateModuleDto : EntityDto<long> { public const int MaxLength = 255; [Required] [StringLength(MaxLength)] public string DisplayName { get; set; } [Required] [StringLength(MaxLength)] public string Name { get; set; } [Required] [StringLength(MaxLength)] public string Url { get; set; } [StringLength(MaxLength)] public string HotKey { get; set; } public int ParentId { get; set; } public bool RequiresAuthentication { get; set; } public bool IsAutoExpand { get; set; } [StringLength(MaxLength)] public string IconName { get; set; } public int Status { get; set; } [Required] [StringLength(MaxLength)] public string ParentName { get; set; } [StringLength(MaxLength)] public string RequiredPermissionName { get; set; } public int SortNo { get; set; } } }
- 這個DTO類在創建和更新模塊信息的時候被使用,用來從頁面獲取模塊信息。
- 類中的屬性定義了數據註解(如[Required])用來定義有效性驗證。ABP會自動校驗DTO的數據有效性。
6. 為什麼需要通過dto進行數據傳輸?
一般來說,使用DTO進行數據傳輸具有以下好處。
- 數據隱藏
- 序列化和延遲載入問題
- ABP對DTO提供了約定類以支持驗證
- 參數或返回值改變,通過Dto方便擴展
- DTO類被用來在 基礎設施層 和 應用層 傳遞數據.
7.Dto規範 (靈活應用)
- ABP建議命名輸入/輸出參數為:MethodNameInput和MethodNameOutput
- 併為每個應用服務方法定義單獨的輸入和輸出DTO(如果為每個方法的輸入輸出都定義一個dto,那將有一個龐大的dto類需要定義維護。一般通過定義一個公用的dto進行共用)
- 即使你的方法只接受/返回一個參數,也最好是創建一個DTO類
- 一般會在對應實體的應用服務文件夾下新建Dtos文件夾來管理Dto類。
定義完DTO,是不是腦袋有個疑問,我在用DTO在展現層與應用服務層進行數據傳輸,但最終這些DTO都需要轉換為實體才能與資料庫直接打交道啊。如果每個dto都要自己手動去轉換成對應實體,這個工作量也是不可小覷啊。
聰明如你,你肯定會想肯定有什麼方法來減少這個工作量。
三、使用AutoMapper自動映射DTO與實體
1.簡要介紹AutoMapper
開始之前,如果對AutoMapper不是很瞭解,建議看下這篇文章AutoMapper小結。
AutoMapper的使用步驟,簡單總結下:
- 創建映射規則(Mapper.CreateMap<source, destination>();)
- 類型映射轉換(Mapper.Map<source,destination>(sourceModel))
在Abp中有兩種方式創建映射規則:
- 特性數據註解方式:
- AutoMapFrom、AutoMapTo 特性創建單向映射
- AutoMap 特性創建雙向映射
- 代碼創建映射規則:
- Mapper.CreateMap<source, destination>();
2.為Module實體相關的Dto定義映射規則
ModuleDto、CreateUpdateModuleDto中的屬性名與Module實體的屬性命名一致,且只需要從Dto映射到實體,不需要反向映射。所以通過AutoMapTo創建單向映射即可。
[AutoMapTo(typeof(Module))] //定義單向映射 public class ModuleDto:EntityDto<long> { ... } [AutoMapTo(typeof(Module))] //定義單向映射 public class CreateUpdateModuleDto : EntityDto<long> { ... }
四、定義IModuleAppService介面
1. 右鍵單擊“Dto”文件夾,然後選擇“添加” > “新建項”,在彈出對話框中選擇“介面”。為應用服務定義一個名為 IModuleAppService
的介面。代碼如下。
using Abp.Application.Services; using Abp.Application.Services.Dto; using ABP.TPLMS.Modules.Dto; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; namespace ABP.TPLMS.Modules { public interface IModuleAppService : IApplicationService { Task CreateAsync(CreateUpdateModuleDto input); Task UpdateAsync(CreateUpdateModuleDto input); Task<ListResultDto<ModuleDto>> GetAllAsync(); Task DeleteAsync(int Id); void Delete(int Id); } }
從上面的代碼中我們仔細看一下方法的參數及返回值,大家可能會發現並未直接使用Module實體對象。這是為什麼呢?因為展現層與應用服務層是通過Data Transfer Object(DTO)進行數據傳輸。
五、實現IModuleAppService
對於具體的業務來講,只是簡單的增刪該查,實現起來就很簡單了。代碼如下:
using Abp.Application.Services; using Abp.Application.Services.Dto; using Abp.Domain.Repositories; using ABP.TPLMS.Entitys; using ABP.TPLMS.Modules.Dto; using AutoMapper; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; namespace ABP.TPLMS.Modules { public class ModuleAppService : ApplicationService, IModuleAppService { private readonly IRepository<Module> _moduleRepository; public ModuleAppService(IRepository<Module> moduleRepository) { _moduleRepository = moduleRepository; } public Task CreateAsync(CreateUpdateModuleDto input) { var module = Mapper.Map<Module>(input); return _moduleRepository.InsertAsync(module); } public Task UpdateAsync(CreateUpdateModuleDto input) { var module = Mapper.Map<Module>(input); return _moduleRepository.UpdateAsync(module); } public async Task<ListResultDto<ModuleDto>> GetAllAsync() { var books = await _moduleRepository.GetAllListAsync(); return new ListResultDto<ModuleDto>(ObjectMapper.Map<List<ModuleDto>>(books)); } public async Task DeleteAsync(int Id) { await _moduleRepository.DeleteAsync(Id); } public void Delete(int Id) { _moduleRepository.Delete(Id); } } }