在 上一篇文章 中,我们使用 ABP 命令行工具构建了一个 ABP 后台项目,本次实验,我们将在项目中构建简单实现 CRUD 功能的 WEB API。
建立域对象 ABP 框架提倡使用面向领域的设计方法,也对该方法提供了比较全面的支持。 因此我们在开发 WEB API时,首先要思考、设计、开发的是域模型。 在本次 WEB API 中,我们需要建立一个名为 Book 的域模型,表示在电商平台中书的(简化)概念。
首先,选择 Acme.BookStoreService.Domain.Shared 项目建立一个枚举类型: BookType, 该类型不需要持久化,但需要在各个层次的项目中共享。
新建名为 Books 的目录,然后在目录中新建一个枚举类型 BookType (BookType.cs), 内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 using System;using System.Collections.Generic;using System.Text;namespace Acme.BookStoreService.Books { public enum BookType { Undefined, Adventure, Biography, Dystopia, Fantastic, Horror, Science, ScienceFiction, Poetry } }
其次,选择 Acme.BookStoreService.Domain 项目,在其中新建名为 Books 的目录。然后在该目录中新建一个名为 Book 的类(Book.cs), 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 using System;using System.Collections.Generic;using System.Text;using Volo.Abp.Domain.Entities.Auditing;namespace Acme.BookStoreService.Books { public class Book: AuditedAggregateRoot<Guid> { public string Name { get ; set ; } public BookType Type { get ; set ; } public DateTime PublishDate { get ; set ; } public float Price { get ; set ; } protected Book ( ) { } public Book (Guid id, string name, BookType type, DateTime publishDate, float price ) : base (id ) { Name = name; Type = type; PublishDate = publishDate; Price = price; } } }
可以看到,我们新建的类继承了 ABP 框架中的 AuditedAggregateRoot 类,该类用来表达领域设计中聚合根的概率,并实现了审计,乐观锁的功能。 泛型 ““ 指明该类在持久存储(数据库)存储时使用 Guid 类型作为表的主键,主机名为 “Id”。
建立持久化映射关系(O/R Mapping) 在 ABP 中,持久化相关的逻辑被封装在 Acme.BookStoreService.EntityFrameworkCore 中,该项目可以看作是对 EF Core 的扩展。
选择 Acme.BookStoreService.EntityFrameworkCore 项目, 在 BookStoreServiceDbContext 中增加一个属性:
1 public DbSet<Book> Books { get ; set ; }
然后在 BookStoreServiceEfCoreEntityExtensionMappings 中定义对象和表映射的细节信息, 在 OneTimeRunner.Run 的 Lambda 中增加如下代码:
1 2 3 4 5 6 builder.Entity<Book>(b => { b.ToTable(BookStoreServiceConsts.DbTablePrefix + "Book" , BookStoreServiceConsts.DbSchema); b.ConfigureByConvention(); b.Property(x => x.Name).IsRequired().HasMaxLength(128 ); });
更新数据库结构 修改完以上的代码后,我们需要修改对应的数据库结构,加入新增的,用于存储图书信息的表。 执行:
设置 Acme.BookStoreService.DbMigrator 为启动项目
设置 Acme.BookStoreService.EntityFrameworkCore.DbMigrations 为包管理控制台的默认项目,然后在包管理控制台运行
在包管理控制台运行中依次运行:
1 2 Add-Migration "Created_Book_Entity“ Update-Database
Add-Migration 后面跟的字符串是对本次数据更改操作的一个命名,建议使用一定的命名标准,方便以后数据库的版本管理。
开发应用服务 在 ABP 中,我们使用应用服务来作为领域层对外的接口。 可以封装复杂的业务逻辑,并将领域模型与具体的展现、交互形式隔离开来。
在本例中,我们将建两个 DTO 对象和一个服务。
选中 Acme.BookStoreService.Application.Contracts 项目,新建名为 Books 的目录,然后在目录中新建两个 DTO:
BookDto.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 using System;using System.Collections.Generic;using System.Text;using Volo.Abp.Application.Dtos;namespace Acme.BookStoreService.Books { public class BookDto : AuditedEntityDto<Guid> { public string Name { get ; set ; } public BookType Type { get ; set ; } public DateTime PublishDate { get ; set ; } public float Price { get ; set ; } } }
CreateUpdateBookDto:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 using System;using System.ComponentModel.DataAnnotations;using System.Text;namespace Acme.BookStoreService.Books { public class CreateUpdateBookDto { [Required ] [StringLength(128) ] public string Name { get ; set ; } [Required ] public BookType Type { get ; set ; } = BookType.Undefined; [Required ] public DateTime PublishDate { get ; set ; } [Required ] public float Price { get ; set ; } } }
然后再定义一个服务接口: IBookAppService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 using System;using System.Collections.Generic;using System.Text;using Volo.Abp.Application.Dtos;using Volo.Abp.Application.Services;namespace Acme.BookStoreService.Books { public interface IBookAppService : ICrudAppService< //定义了CRUD方法 BookDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateBookDto, CreateUpdateBookDto> { } }
定义完接口后,我们来定义实现。 选择 Acme.BookStoreService.Application 项目,在项目中新建 Books 目录,然后在 Books 目录中新建名为 BookStoreServiceAppService 的类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 using System;using System.Collections.Generic;using System.Text;using Acme.BookStoreService.Books;using Acme.BookStoreService.Localization;using Volo.Abp.Application.Dtos;using Volo.Abp.Application.Services;using Volo.Abp.Domain.Repositories;namespace Acme.BookStoreService { public abstract class BookStoreServiceAppService : CrudAppService<Book, BookDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateBookDto, CreateUpdateBookDto>, IBookAppService { protected BookStoreServiceAppService (IRepository<Book, Guid> repository ) :base (repository ) { } } }
可以看到,框架已经帮助我们实现一个单实体的 CRUD 操作。 我们需要做的不多。
最后,我们需要告诉框架 Book 领域实体对象和 Dto 是怎么映射的,这个定义在 Acme.BookStoreService.Application 项目的 BookStoreServiceApplicationAutoMapperProfile 类中, 如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 using Acme.BookStoreService.Books;using AutoMapper;namespace Acme.BookStoreService { public class BookStoreServiceApplicationAutoMapperProfile : Profile { public BookStoreServiceApplicationAutoMapperProfile ( ) { CreateMap<Book, BookDto>(); CreateMap<CreateUpdateBookDto, Book>(); } } }
在 ABP 中,应用服务层中所说的服务并不是 WEB API, 而是存业务的操作,与具体的展现形式,或是存储形式无关
因为 ABP 会自动根据应用服务层中的接口生成 WEB API, 所以在本例中我们不需要单独为 WEB API 进行编程。
现在,可以设置 Acme.BookStoreService.HttpApi.Host 为启动项目,然后运行项目了。