从零开始进行ABP项目开发(六)——继续应用层的开发

现在项目的架构已经基本搭好了,需要完善应用层,将所需要的功能补充完整。由于我们的数据库中已经保存了诗和诗人的数据,这部分数据不需要通过我们的应用维护,我们需要的功能是针对诗和诗人的查询以及对诗的分类的维护。主要功能如下:

  • 诗人查询:按姓名进行模糊查询
  • 根据id获取诗人数据。
  • 诗查询: 按诗人进行查询;按关键字在标题进行模糊查询;按分类进行查询,如果分类是多个,就查询属于所有分类的诗。比如,如果查询条件是“唐诗三百首”和“五言诗”,那么结果应该是唐诗三百首中的五言诗。
  • 根据id获取诗数据。
  • 分类列表:列出所有分类。
  • 分类的增、删:可以增加和删除分类。
  • 查询某一首诗的所有分类。

上面的功能能够基本完成诗和诗人查询分类的功能。上述功能的定义在接口IPoemAppService中定义,在PoemAppService中实现。相关测测试用例在单元测试项目中实现。这些功能的输入输出采用DTO,DTO与实体通过AutoMap进行映射。下面先看一下DTO的定义和映射,然后对重点功能的实现做下说明。

DTO的定义

DTO与实体的映射

以诗为例,说明Dto与实体的映射,诗的实体中包括作者的定义,如下:

    /// <summary>
    /// 诗
    /// Volumn和Num是全唐诗中的卷和序号,
    /// </summary>
    public class Poem : Entity
    {
        ...
        /// <summary>
        /// 作者
        /// </summary>
        public virtual Poet Author { get; set; }
        ... 
    }

这里诗的作者是Poet对象,而我们在输出的时候,希望PoemDto中是诗人的名字,PoemDto的定义如下:

    [AutoMapFrom(typeof(Core.Poems.Poem))]
    public class PoemDto : EntityDto
    {
        public string Title { get; set; }

        public string Content { get; set; }

        public int PoetID { get; set; }

        public string AuthorName { get; set; }

        public string Comments { get; set; }

        public string Volumn { get; set; }

        public string Num { get; set; }
    }

这时,我们需要将实体中Author的Name映射到DTO的AuthorName字段。这需要在PoemApplicationModule模块启动前,加载映射的规则:

namespace ZL.Poem.Application
{
    [DependsOn(typeof(PoemCoreModule), typeof(AbpAutoMapperModule))]
    public class PoemApplicationModule : AbpModule
    {
        ...
        //这里加载Dto到实体的映射规则
        public override void PreInitialize()
        {
            Configuration.Modules.AbpAutoMapper().Configurators.Add(config =>
            {
                config.CreateMap<Core.Poems.Poem, PoemDto>()
                      .ForMember(u => u.AuthorName, options => options.MapFrom(o => o.Author.Name));
            });
        }
        ...
    }
}

在PreInitialize中定义了映射规则,将Author.Name映射到Dto的AuthorName字段。
除了PoemDto,还有PoetDto、CategoryDto、CategoryPoemDto与相应的实体映射,结构比较简单,这里不再描述。

作为输入的DTO

作为输入的DTO有三个:

  • PagedResultRequestDto: 用于定义分页查询输入的Dto。
  • SearchPoetDto: 用于定义诗人查询输入的Dto,从PagedResultRequestDto派生,并包括查询关键字。
  • SearchPoemDto: 用于定义诗查询输入的Dto,从PagedResultRequestDto派生,包括诗人姓名、查询关键字和分类数组。

代码如下:

using Abp.Application.Services.Dto;

namespace ZL.Poem.Application.Poems
{
    /// <summary>
    /// 分页查询传入参数
    /// </summary>
    public class PagedResultRequestDto : IPagedResultRequest
    {
        /// <summary>
        /// 跳过的记录数
        /// </summary>
        public int SkipCount { get; set; }

        /// <summary>
        /// 返回的最大记录数
        /// </summary>
        public int MaxResultCount { get; set; }
    }
}

namespace ZL.Poem.Application.Poems
{
    public class SearchPoetDto : PagedResultRequestDto
    {
        public string Keyword { get; set; }
    }
}
namespace ZL.Poem.Application.Poems
{
    public class SearchPoemDto : PagedResultRequestDto
    {
        public string Keyword { get; set; }

        public string AuthorName { get; set; }

        public string[] Categories { get; set; }
    }
}

功能实现说明

功能的定义在接口IPoemAppService中定义,在PoemAppService中实现。相关测测试用例在单元测试项目中实现。这里只对重点功能的实现进行说明,按照定义、实现、测试的顺序列出相关代码。

获取诗人分页

定义:

        /// <summary>
        /// 获取诗人分页
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        PagedResultDto<PoetDto> GetPagedPoets(PagedResultRequestDto dto);

输入PagedResultRequestDto。

实现:

        public PagedResultDto<PoetDto> GetPagedPoets(PagedResultRequestDto dto)
        {
            var count = _poetRepository.Count();
            var lst = _poetRepository.GetAll().OrderBy(o => o.Id).PageBy(dto).ToList();

            return new PagedResultDto<PoetDto>
            {
                TotalCount = count,
                Items = lst.MapTo<List<PoetDto>>()
            };
        }

输出是Abp定义的PagedResultDto<T>结构,包括两部分:记录总数和查询出的结果。

相关测试:

        [Fact]
        public void GetPoets_Test()
        {
            // Act
            var output = _appService.GetPagedPoets(new PagedResultRequestDto { MaxResultCount = 20, SkipCount = 0 });

            // Assert
            output.Items.Count.ShouldBe(2);
            output.Items[0].Name.ShouldBe("李白");
        }

测试的数据在TestDataBuilder中创建。

查询诗:

定义:


        /// <summary>
        /// 按条件查询诗,条件是关键字(模糊查询),作者(精确查询),分类(属于所有分类)
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        PagedResultDto<PoemDto> SearchPoems(SearchPoemDto dto);

输入的条件为三部分:

  • 关键字:模糊查询
  • 作者姓名:精确查询
  • 分类数组: 属于输入的所有分类

如果所有查询条件为空,查询结果为所有记录。如果查询条件不为空,查询结果是各查询条件的交集。

实现:

        public PagedResultDto<PoemDto> SearchPoems(SearchPoemDto dto)
        {
            var res = _poemRepository.GetAllIncluding(c => c.Author);
            //
            if (!string.IsNullOrEmpty(dto.Keyword))
            {
                res = res.Where(o => o.Title.Contains(dto.Keyword));
            }
            if (!string.IsNullOrEmpty(dto.AuthorName))
            {
                res = res.Where(o => o.Author.Name == dto.AuthorName);
            }
            if (dto.Categories != null)
            {
                foreach (var category in dto.Categories)
                {
                    res = res.Where(o => o.PoemCategories.Any(q => q.Category.CategoryName == category));
                }
            }

            var count = res.Count();
            var lst = res.OrderBy(o => o.Id).PageBy(dto).ToList();

            return new PagedResultDto<PoemDto>
            {
                TotalCount = count,
                Items = lst.MapTo<List<PoemDto>>()
            };
        }

测试:

        [Fact]
        public void SearchPomts_Test()
        {
            // Act
            var output = _appService.SearchPoems(new SearchPoemDto { AuthorName = "李白", MaxResultCount = 20, SkipCount = 0 });

            // Assert
            output.Items.Count.ShouldBe(2);

            output.Items[0].Title.ShouldBe("静夜思");
        }

        [Fact]
        public void SearchPomtWithKeys_Test()
        {
            // Act
            var output = _appService.SearchPoems(new SearchPoemDto { Keyword = "静", MaxResultCount = 20, SkipCount = 0 });

            // Assert
            output.Items.Count.ShouldBe(1);

            output.Items[0].Title.ShouldBe("静夜思");
        }
        [Fact]
        public void SearchPomtWithCategories_Test()
        {
            var output = _appService.SearchPoems(new SearchPoemDto { Categories = new string[] { "小学古诗" }, MaxResultCount = 20, SkipCount = 0 });
            output.Items.Count.ShouldBe(3);
        }

这个功能的组合比较多,因此需要相应的测试用例,这里没有列全,只列出了部分测试用例。

小结

通过本讲,我们了解到:

  • 如何实现DTO与实体间的映射
  • 使用PagedResultRequestDto定义分页查询条件
  • 使用PagedResultDto<T> 定义分页查询结果
  • 用单元测试检验功能

下一步工作

下一步我们要定义Web Api模块,将应用层的功能通过Web API 发布。然后,我们就可以开发客户端程序。

本文同步发布在我的个人网站 http://www.jiagoushi.cn

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容