(精華)2020年8月22日 ABP vNext DTO在應用層的使用

愚公搬程式碼發表於2020-08-22

我們繼續應用層的開發,首先建立負責在應用層和展示層之間傳遞資料的物件,也就是DTO。

使用DTO的原因

為什麼需要DTO呢?有如下幾個原因。

  1. 隔離領域層與表示層,使領域層和表示層可以獨立演化,互相不受影響。
  2. 資料隱藏,領域層的某些資料需要對錶示層隱藏(比如使用者密碼),在定義DTO時,可以不設定隱藏欄位的對映,實現資料隱藏。DTO只返回表示層需要的資料,不多也不少。
  3. 避免序列化問題。領域物件中會帶有迴圈引用,比如詩人Poet會引用詩Poems,而詩Poem中又引用了詩人Poet,這種迴圈引用在序列化時會出現問題。

ABP vNext中DTO和實體的對應關係

ABP vNext使用對映Profile定義DTO和實體之間的對映關係,這與以前版本使用AutoMap標籤是不同的,對映檔案如下:
以前使用如下:

[AutoMapFrom(typeof(Products))]
    public class ProductDto:EntityDto
    {
        public string ProductName { get; set; }
        public string ProductImage { get; set; }
        public double Price { get; set; }
        public double OrgPrice { get; set; }
        public string Decoration { get; set; }
        public string Sizes { get; set; }
        public int ClickTimes { get; set; }
        public int SaleTimes { get; set; }
        public string DetailText { get; set; }
    }

現在使用如下:

using AutoMapper;
using ZL.AbpNext.Poem.Core.Poems;

namespace ZL.AbpNext.Poem.Application.Poems
{
    public class PoemAppAutoMapperProfile : Profile
    {
        public PoemAppAutoMapperProfile()
        {
            CreateMap<Poet, PoetDto>();
            CreateMap<Core.Poems.Poem, PoemDto>();
            CreateMap<Category, CategoryDto>();
            CreateMap<CategoryPoem, CategoryPoemDto>();
        }
    }
}

我們已經建立了領域層,並且使用EF實現了對資料庫的訪問,我們還建立了用於資料交換的DTO,現在繼續應用層的開發。我們需要實現如下基本需求:

  • 詩人查詢:按姓名進行模糊查詢
  • 根據id獲取詩人資料。
  • 詩查詢:
    按詩人進行查詢;按關鍵字在標題進行模糊查詢;按分類進行查詢,如果分類是多個,就查詢屬於所有分類的詩。比如,如果查詢條件是“唐詩三百首”和“五言詩”,那麼結果應該是唐詩三百首中的五言詩。
  • 根據id獲取詩資料。
  • 分類列表:列出所有分類。
  • 分類的增、刪:可以增加和刪除分類。
  • 查詢某一首詩的所有分類。

上面的功能能夠基本完成詩和詩人查詢分類的功能。

上述功能的定義在介面IPoemAppService中定義,在PoemAppService中實現。IPoemAppService程式碼如下:


using System.Collections.Generic;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;

namespace ZL.AbpNext.Poem.Application.Poems
{
    public interface IPoemAppService:IApplicationService
    {
        /// <summary>
        /// 獲取詩人分頁
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        PagedResultDto<PoetDto> GetPagedPoets(PagedResultRequestDto dto);

        /// <summary>
        /// 查詢詩人,按名字模糊查詢
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        PagedResultDto<PoetDto> SearchPoets(SearchPoetDto dto);

        /// <summary>
        /// 獲取詩的分頁查詢
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        PagedResultDto<PoemDto> GetPagedPoems(PagedResultRequestDto dto);


        /// <summary>
        /// 按條件查詢詩,條件是關鍵字(模糊查詢),作者(精確查詢),分類(屬於所有分類)
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        PagedResultDto<PoemDto> SearchPoems(SearchPoemDto dto);

        /// <summary>
        /// 增加分類,如果已經存在,不增加,返回-1,如果增加成功,返回新增記錄的id
        /// </summary>
        /// <param name="category"></param>
        /// <returns></returns>
        CategoryDto AddCategory(CategoryDto category);

        /// <summary>
        /// 刪除分類
        /// </summary>
        /// <param name="category"></param>
        void DeleteCategory(CategoryDto category);

        /// <summary>
        /// 分類列表
        /// </summary>
        /// <returns></returns>
        List<CategoryDto> GetAllCategories();

        /// <summary>
        /// 將詩關聯到分類
        /// </summary>
        /// <param name="categoryPoem"></param>
        void AddPoemToCategory(CategoryPoemDto categoryPoem);

        /// <summary>
        /// 解除詩和分類的關聯
        /// </summary>
        /// <param name="categoryPoem"></param>
        void RemovePoemFromCategory(CategoryPoemDto categoryPoem);


        List<CategoryPoemDto> GetCategoryPoems();

        /// <summary>
        /// 列出詩的分類
        /// </summary>
        /// <param name="poemid"></param>
        /// <returns></returns>
        List<CategoryDto> GetPoemCategories(int poemid);

        /// <summary>
        /// 列出分類的詩
        /// </summary>
        /// <param name="categoryid"></param>
        /// <returns></returns>
        List<PoemDto> GetPoemsOfCategory(int categoryid);


        PoetDto AddPoet(PoetDto poet);
    }
}

現在我們可以編寫PoemAppService,這個類的定義如下:

public class PoemAppService : ApplicationService, IPoemAppService
    {
        private readonly IRepository<Core.Poems.Poem> _poemRepository;
        private readonly IRepository<Category> _categoryRepository;
        private readonly IRepository<Poet> _poetRepository;
        private readonly IRepository<CategoryPoem> _categoryPoemRepository;
        public PoemAppService(IRepository<Core.Poems.Poem> poemRepository
            , IRepository<Category> categoryRepository
            , IRepository<Poet> poetRepository
            , IRepository<CategoryPoem> categoryPoemRepository)
        {
            _poemRepository = poemRepository;
            _categoryRepository = categoryRepository;
            _poetRepository = poetRepository;
            _categoryPoemRepository = categoryPoemRepository;
        }

這裡我們使用Abp提供的通用的IRepository,編寫一般的方法可以滿足需求,比如增加,刪除等等:

        public CategoryDto AddCategory(CategoryDto category)
        {
            var cate = _categoryRepository.FirstOrDefault(o => o.CategoryName == category.CategoryName);

            if (cate == null)
            {
                cate= _categoryRepository.InsertAsync(new Category { CategoryName = category.CategoryName },true).Result;
            }
            return ObjectMapper.Map<Category,CategoryDto>(cate); 
            
        }

        public List<CategoryDto> GetAllCategories()
        {
            return ObjectMapper.Map<List<Category>, List<CategoryDto>>(_categoryRepository.ToList());
        }

        public void DeleteCategory(CategoryDto category)
        {
                var cat = _categoryRepository.FirstOrDefault(o => o.Id == category.Id);
                if (cat != null)
                {
                    _categoryRepository.DeleteAsync(cat, true);
                }
         }

這裡需要說明的是,Abp 將ApplicationService中的方法作為工作單元處理,所以不需要像在一開始控制檯應用中那樣顯示使用UnitWork。

當我們進行稍微複雜一些的開發時,發現通用的IRepository不夠用了,比如,如果獲取是的分類,需要寫成這樣:

public List<CategoryDto> GetPoemCategories(int poemid)
{
    var lst = _categoryPoemRepository.Where(p => p.PoemId == poemid);
    var categories = new List<Category>();
    foreach (var cp in lst)
    {
        var cate = _categoryRepository.GetAsync(o => o.Id == cp.CategoryId).Result;
        categories.Add(cate);
    }

    return ObjectMapper.Map<List<Category>, List<CategoryDto>>(categories);
}

對於更復雜的一些功能,比如模糊查詢,使用預設的IRepository甚至無法實現。下節我們開發定製的Repository。

相關文章