当前位置: 首页 > news >正文

ABP-Book Store Application中文讲解 - Part 8: Authors: Application Layer

ABP-Book Store Application中文讲解 - Part 8: Authors: Application Layer

本章主要讲解手撸AuthorAppService中的增删改查,而不借用CrudAppService。

ABP-Book Store Application中文讲解 - Part 7: Authors: Database Integration

 1. 汇总

ABP-Book Store Application中文讲解-汇总-CSDN博客

2. 前一章 

ABP-Book Store Application中文讲解 - Part 6: Authors: Domain Layer-CSDN博客

项目之间的引用关系。

​​

注意命名规范: I***AppService,***AppService 

1. 创建IAuthorAppSevice接口和Dtos

在Acme.BookStore.Application.Contracts中创建Authors目录,然后在目录中创建IAuthorAppService接口并继承IApplicationService。

IApplicationService是所有应用程序服务的常规接口,ABP会自动识别此服务。

1.1 创建DTOs

在Authors目录中创建Dtos目录,目录中创建AuthorDto.cs, CreateAuthorDto.cs, UpdateAuthorDto.cs和GetAuthorListDto.cs。

你可以发现CreateAuthorDto.cs和UpdateAuthorDto.cs字段一样,此处你爷可以只创建一个CreateOrUpdateAuthorDto.cs去替换上面两个,但是ABP推荐分开他们,避免紧耦合。

比如当我们要扩展记录LastmodifiedDate/lastModifiedBy的时候,只需要更改UpdateAuthorDto.cs即可。


using System;
using Volo.Abp.Application.Dtos;namespace Acme.BookStore.Authors.Dtos;
public class AuthorDto : EntityDto<Guid>
{public string Name { get; set; }public DateTime BirthDate { get; set; }/// <summary>/// 笔名?/// </summary>public string ShortBio { get; set; }
}

using System;
using System.ComponentModel.DataAnnotations;
using static Acme.BookStore.BookStoreConsts;namespace Acme.BookStore.Authors.Dtos;public class CreateAuthorDto
{[Required][StringLength(AuthorConsts.MaxNameLength)]public string Name { get; set; } = string.Empty;[Required]public DateTime BirthDate { get; set; }/// <summary>/// 笔名?/// </summary>public string? ShortBio { get; set; }
}
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Application.Dtos;
using static Acme.BookStore.BookStoreConsts;namespace Acme.BookStore.Authors.Dtos;public class UpdateAuthorDto : EntityDto<Guid>
{[Required][StringLength(AuthorConsts.MaxNameLength)]public string Name { get; set; } = string.Empty;[Required]public DateTime BirthDate { get; set; }/// <summary>/// 笔名?/// </summary>public string? ShortBio { get; set; }
}
using Volo.Abp.Application.Dtos;namespace Acme.BookStore.Authors.Dtos;/// <summary>
/// PagedAndSortedResultRequestDto是一个标准的分页类,里面有MaxResultCount->int,SkipCount->int, Sorting->string
/// </summary>
public class GetAuthorListDto : PagedAndSortedResultRequestDto
{/// <summary>/// 用来搜索作者Author/// </summary>public string? Filter { get; set; }
}

1.2 创建IAuthorAppService

在Authors目录中创建IAuthorAppService接口,并定义增删改查functions。

using Acme.BookStore.Authors.Dtos;
using System;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;namespace Acme.BookStore.Authors;
public interface IAuthorAppService : IApplicationService
{Task<AuthorDto> GetAsync(Guid id);Task<PagedResultDto<AuthorDto>> GetListAsync(GetAuthorListDto input);Task<AuthorDto> CreateAsync(CreateAuthorDto input);Task UpdateAsync(UpdateAuthorDto input);Task DeleteAsync(Guid id);
}

目录结构如下:

 

2. 创建IAuthorAppService的实现类AuthorAppService

在 Acme.BookStore.Application中创建Authors目录,然后创建IAuthorAppService的实现类AuthorAppService.cs,并继承BookStoreAppService.

注意BookStoreAppService需要放在接口IAuthorAppService的前面,否则报错CS1722。

2.1 AutoMapper

在Acme.BookStore.Application中的BookStoreApplicationAutoMapperProfile.cs 中添加一下代码:

CreateMap<Author, AuthorDto>();

2.2 创建构造函数,利用DI引入IAuthorRepository和AuthorManager

在构造函数中引入 IAuthorRepository和AuthorManager。

   private readonly IAuthorRepository _authorRepository;private readonly AuthorManager _authorManager;public AuthorAppService(IAuthorRepository authorRepository, AuthorManager authorManager){_authorRepository = authorRepository;_authorManager = authorManager;}

2.3 方法实现

2.3.1 GetAsync根据id查询Author

    public async Task<AuthorDto> GetAsync(Guid id){var author = await _authorRepository.GetAsync(id);// 此处如果移除await会报CS1988的警告,虽然代码不报错,但是会被API被截断的风险。return ObjectMapper.Map<Author, AuthorDto>(author);}

2.3.2 GetListAsync

    public async Task<PagedResultDto<AuthorDto>> GetListAsync(GetAuthorListDto input){if (string.IsNullOrEmpty(input.Sorting)){input.Sorting = nameof(Author.Name);}var authors = await _authorRepository.GetListAsync(skipCount: input.SkipCount, maxResultCount: input.MaxResultCount, sorting: input.Sorting, filter: input.Filter);var totalCount = string.IsNullOrEmpty(input.Filter) ?await _authorRepository.CountAsync(): await _authorRepository.CountAsync(x => x.Name.Contains(input.Filter));return new PagedResultDto<AuthorDto>(totalCount, ObjectMapper.Map<List<Author>, List<AuthorDto>>(authors));}

2.3.3 CreateAsync

    public async Task<AuthorDto> CreateAsync(CreateAuthorDto input){// 利用Domain Service创建Authorvar author = await _authorManager.CreateAsync(name: input.Name, birthDate: input.BirthDate, shortBio: input.ShortBio);// 利用Repository插入数据await _authorRepository.InsertAsync(author);// 会自动回填Id到authorreturn ObjectMapper.Map<Author, AuthorDto>(author);}

2.3.4 UpdateAsync

    public async Task UpdateAsync(UpdateAuthorDto input){var author = await _authorRepository.GetAsync(input.Id);if (author.Name != input.Name){// 利用Domain Service更新名字await _authorManager.ChangeNameAsync(author, input.Name);}author.BirthDate = input.BirthDate;author.ShortBio = input.ShortBio;await _authorRepository.UpdateAsync(author);}

2.3.5 DeleteAsync

    public async Task DeleteAsync(Guid id){await _authorRepository.DeleteAsync(id);}

2.3 定义Permissions

2.3.1 定义Permission

在Acme.BookStore.Application.Contracts项目中的Permissions中,打开BookStorePermissions.cs,添加如下代码:

    /// <summary>/// 定义Authors权限/// </summary>public static class Authors{public const string Default = GroupName + ".Authors";// 控制Authors页面权限public const string Create = Default + ".Create";// 控制Create button的隐藏显示public const string Edit = Default + ".Edit";// 控制Edit button的隐藏显示public const string Delete = Default + ".Delete";// Delete button的隐藏显示}

 2.3.2 添加本地化资源

在Acme.BookStore.Domain.Shared中的Localization\BookStore目录中找到en.json和zh-Hans.json,分别添加如下内容:

en.json

   "Permission:Authors": "Author Management","Permission:Authors.Create": "Creating new authors","Permission:Authors.Edit": "Editing the authors","Permission:Authors.Delete": "Deleting the authors"

zh-Hans.json 

  "Permission:Authors": "作者管理","Permission:Authors.Create": "新建作者","Permission:Authors.Edit": "编辑作者","Permission:Authors.Delete": "删除作者"

2.3.3 加入Permissions组

在Acme.BookStore.Application.Contracts项目中的Permissions中,打开BookStorePermissionDefinitionProvider.cs,添加如下代码:

        var authorsPermission = bookStoreGroup.AddPermission(BookStorePermissions.Authors.Default, L("Permission:Authors"));authorsPermission.AddChild(BookStorePermissions.Authors.Create, L("Permission:Authors.Create"));authorsPermission.AddChild(BookStorePermissions.Authors.Edit, L("Permission:Authors.Edit"));authorsPermission.AddChild(BookStorePermissions.Authors.Delete, L("Permission:Authors.Delete"));

2.3.4 BookStorePermissions完整代码

namespace Acme.BookStore.Permissions;public static class BookStorePermissions
{public const string GroupName = "BookStore";// other permissions.../// <summary>/// 定义Books权限/// </summary>public static class Books{public const string Default = GroupName + ".Books";// 控制Book页面权限public const string Create = Default + ".Create";// 控制Create button的隐藏显示public const string Edit = Default + ".Edit";// 控制Edit button的隐藏显示public const string Delete = Default + ".Delete";// Delete button的隐藏显示}/// <summary>/// 定义Authors权限/// </summary>public static class Authors{public const string Default = GroupName + ".Authors";// 控制Authors页面权限public const string Create = Default + ".Create";// 控制Create button的隐藏显示public const string Edit = Default + ".Edit";// 控制Edit button的隐藏显示public const string Delete = Default + ".Delete";// Delete button的隐藏显示}
}

2.3.5 BookStorePermissionDefinitionProvider完整代码

using Acme.BookStore.Localization;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization;namespace Acme.BookStore.Permissions;public class BookStorePermissionDefinitionProvider : PermissionDefinitionProvider
{public override void Define(IPermissionDefinitionContext context){var bookStoreGroup = context.AddGroup(BookStorePermissions.GroupName, L("Permission:BookStore"));var booksPermission = bookStoreGroup.AddPermission(BookStorePermissions.Books.Default, L("Permission:Books"));booksPermission.AddChild(BookStorePermissions.Books.Create, L("Permission:Books.Create"));booksPermission.AddChild(BookStorePermissions.Books.Edit, L("Permission:Books.Edit"));booksPermission.AddChild(BookStorePermissions.Books.Delete, L("Permission:Books.Delete"));var authorsPermission = bookStoreGroup.AddPermission(BookStorePermissions.Authors.Default, L("Permission:Authors"));authorsPermission.AddChild(BookStorePermissions.Authors.Create, L("Permission:Authors.Create"));authorsPermission.AddChild(BookStorePermissions.Authors.Edit, L("Permission:Authors.Edit"));authorsPermission.AddChild(BookStorePermissions.Authors.Delete, L("Permission:Authors.Delete"));}private static LocalizableString L(string name){return LocalizableString.Create<BookStoreResource>(name);}
}

2.4 添加Authorize属性到AuthorAppService

在AuthorAppService.cs类上添加[Authorize(BookStorePermissions.Authors.Default)]

在CreateAsync上面添加    [Authorize(BookStorePermissions.Authors.Create)]

在UpdateAsync上面添加    [Authorize(BookStorePermissions.Authors.Edit)]

在DeleteAsync上面添加    [Authorize(BookStorePermissions.Authors.Delete)]

AuthorAppService.cs完整代码:

using Acme.BookStore.Authors.Dtos;
using Acme.BookStore.Permissions;
using Microsoft.AspNetCore.Authorization;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Repositories;namespace Acme.BookStore.Authors;[Authorize(BookStorePermissions.Authors.Default)]
public class AuthorAppService : BookStoreAppService, IAuthorAppService
{private readonly IAuthorRepository _authorRepository;private readonly AuthorManager _authorManager;public AuthorAppService(IAuthorRepository authorRepository, AuthorManager authorManager){_authorRepository = authorRepository;_authorManager = authorManager;}public async Task<AuthorDto> GetAsync(Guid id){var author = await _authorRepository.GetAsync(id);// 此处如果移除await会报CS1988的警告,虽然代码不报错,但是会被API被截断的风险。return ObjectMapper.Map<Author, AuthorDto>(author);}public async Task<PagedResultDto<AuthorDto>> GetListAsync(GetAuthorListDto input){if (string.IsNullOrEmpty(input.Sorting)){input.Sorting = nameof(Author.Name);}var authors = await _authorRepository.GetListAsync(skipCount: input.SkipCount, maxResultCount: input.MaxResultCount, sorting: input.Sorting, filter: input.Filter);var totalCount = string.IsNullOrEmpty(input.Filter) ?await _authorRepository.CountAsync(): await _authorRepository.CountAsync(x => x.Name.Contains(input.Filter));return new PagedResultDto<AuthorDto>(totalCount, ObjectMapper.Map<List<Author>, List<AuthorDto>>(authors));}[Authorize(BookStorePermissions.Authors.Create)]public async Task<AuthorDto> CreateAsync(CreateAuthorDto input){// 利用Domain Service创建Authorvar author = await _authorManager.CreateAsync(name: input.Name, birthDate: input.BirthDate, shortBio: input.ShortBio);// 利用Repository插入数据await _authorRepository.InsertAsync(author);// 会自动回填Id到authorreturn ObjectMapper.Map<Author, AuthorDto>(author);}[Authorize(BookStorePermissions.Authors.Edit)]public async Task UpdateAsync(UpdateAuthorDto input){var author = await _authorRepository.GetAsync(input.Id);if (author.Name != input.Name){// 利用Domain Service更新名字await _authorManager.ChangeNameAsync(author, input.Name);}author.BirthDate = input.BirthDate;author.ShortBio = input.ShortBio;await _authorRepository.UpdateAsync(author);}[Authorize(BookStorePermissions.Authors.Delete)]public async Task DeleteAsync(Guid id){await _authorRepository.DeleteAsync(id);}
}

3. 初始化作者数据

在Acme.BookStore.Domain中的Data目录下找到BookStoreDataSeederContributor.cs,

在构造函数中注入IAuthorRepository authorRepository, AuthorManager authorManager。

        private readonly IAuthorRepository _authorRepository;private readonly AuthorManager _authorManager;public BookStoreDataSeederContributor(IRepository<Book, Guid> bookRepository, IAuthorRepository authorRepository, AuthorManager authorManager){_bookRepository = bookRepository;_authorRepository = authorRepository;_authorManager = authorManager;}

在SeedAsync添加如下代码:

   // 添加Author初始化数据if (await _authorRepository.GetCountAsync() <= 0){var author = await _authorManager.CreateAsync("刘慈欣", new DateTime(1963, 6, 1), "刘慈欣被誉为中国科幻的领军人物,他的作品“三体三部曲”是中国科幻文学的里程碑之作,将中国科幻推上了世界的高度。");await _authorRepository.InsertAsync(author);var author2 = await _authorManager.CreateAsync("梁晓声", new DateTime(1949, 9, 22), "梁晓声,原名梁绍生,中国当代著名作家,中国作家协会会员,1949年9月22日出生于黑龙江省哈尔滨市,毕业于复旦大学 [3] [50],祖籍山东威海市泊于镇温泉寨。他曾创作出版过大量有影响的小说、散文、随笔及影视作品,为中国现当代以知青文学成名的代表作家之一。现居北京,任教于北京语言大学人文学院汉语言文学专业。");await _authorRepository.InsertAsync(author2);}

4. 将Acme.BookStore.DbMigrator设为启动项

将Acme.BookStore.DbMigrator设为启动项,然后F5运行,初始化数据。

5. Tests

5.1 命名规则


为了项目测试用例的统一性,我们需要遵循一定的命名规则,该命名规则有利于我们在用pipeline或者本地Run tests运行测试用例时可以快速定位到那个类或者哪个方法运行报错了。

class的命名规则:****_Tests

例如测试BookAppService.cs,则创建的对应测试用例的class名字是BookAppService_Tests.cs

方法名:Should_***_of_***

例如测试function的名字是BookAppService.cs中的GetList或者GetListAsync, 则名字是Should_Get_List_Of_Book

针对Dto中的Validation,例如Name不能为空,或者长度不能大于多少,也需要遵循统一的命名规则。

 例如测试function的名字是BookAppService.cs中的Create或者CreateAsync, 则名字是Should_Not_Create_A_Book_Without_Name

具体详见:ABP-Book Store Application中文讲解 - Part 4: Integration Tests-CSDN博客

5.2 新建Acme.BookStore.Application.Tests

在Acme.BookStore.Application.Tests中新建文件夹Authors,然后在文件夹中创建AuthorAppService_Tests.cs。代码如下:

using System;
using System.Threading.Tasks;
using Acme.BookStore.Authors.Dtos;
using Shouldly;
using Volo.Abp.Modularity;
using Xunit;namespace Acme.BookStore.Authors;public abstract class AuthorAppService_Tests<TStartupModule> : BookStoreApplicationTestBase<TStartupModule>where TStartupModule : IAbpModule
{private readonly IAuthorAppService _authorAppService;protected AuthorAppService_Tests(){_authorAppService = GetRequiredService<IAuthorAppService>();}[Fact]public async Task Should_Get_All_Authors_Without_Any_Filter(){var result = await _authorAppService.GetListAsync(new GetAuthorListDto());result.TotalCount.ShouldBeGreaterThanOrEqualTo(2);result.Items.ShouldContain(author => author.Name == "刘慈欣");result.Items.ShouldContain(author => author.Name == "梁晓声");}[Fact]public async Task Should_Get_Filtered_Authors(){var result = await _authorAppService.GetListAsync(new GetAuthorListDto { Filter = "刘慈欣" });result.TotalCount.ShouldBeGreaterThanOrEqualTo(1);result.Items.ShouldContain(author => author.Name == "刘慈欣");}[Fact]public async Task Should_Create_A_New_Author(){var name = "Evan Wang";var authorDto = await _authorAppService.CreateAsync(new CreateAuthorDto{Name = name,BirthDate = new DateTime(1987, 01, 07),ShortBio = "A Developer"});authorDto.Id.ShouldNotBe(Guid.Empty);authorDto.Name.ShouldBe(name);}[Fact]public async Task Should_Not_Allow_To_Create_Duplicate_Author(){await Assert.ThrowsAsync<AuthorAlreadyExistsException>(async () =>{await _authorAppService.CreateAsync(new CreateAuthorDto{Name = "刘慈欣",BirthDate = DateTime.Now,ShortBio = "..."});});}//TODO: Test other methods...
}

 

6. 继续学习

相关文章:

  • 苹果企业签名撤销
  • powershell 查当前用户和域名
  • Python 区块链开发实战:从零到一构建智能合约
  • 【手写系列】手写动态代理
  • 软件工程专业的本科生应该具备哪些技能
  • pack 布局管理器
  • Spring 中的disposableBean介绍
  • 点云数据去噪(Point Cloud Processing Toolbox)
  • JVM 内存结构 详解
  • fastadmin fildList 动态下拉框默认选中
  • 【android bluetooth 协议分析 12】【A2DP详解 2】【开启ble扫描-蓝牙音乐卡顿分析】
  • 【知识点】第6章:组合数据类型
  • 时序替换实时?是否必要
  • C++算法训练营 Day7 哈希表及双指针
  • 《汇编语言》第14章 端口——实验14 访问CMOS RAM
  • OpenCV C++ 心形雨动画
  • 灰狼优化算法MATLAB实现,包含种群初始化和29种基准函数测试
  • 从零开始:用Tkinter打造你的第一个Python桌面应用
  • JVMTI 在安卓逆向工程中的应用
  • 解决 WebAssembly 错误:Incorrect response MIME type (Expected ‘application/wasm‘)
  • 南阳做网站哪个好/营销方案设计思路
  • 建设网站公司专业/平台推广是做什么的
  • 对网站的赏析/电子商务网店运营推广
  • 专业做网站广州/百度com百度一下你
  • 营销型网站建设的指导原则不包括/怎么让关键词快速上首页
  • 时尚网站设计/自己做网站流程