ABP框架入门学习(二)——实体层/领域层/应用层实现
上篇文章写到下载模板,模板的结构简单介绍、生成数据库和基础数据,然后并运行了项目,接下来咋们说说项目的扩展,新建一块自己的业务,参照现有的项目架构,我们该怎么一步步增加自己的功能?
一、创建Book实体
前面有说道项目领域层是有两块组成:
TestApp.BookStore.Domain
包含你的实体, 领域服务和其他核心域对象.TestApp.BookStore.Domain.Shared
包含可与客户共享的常量,枚举或其他域相关对象
在TestApp.BookStore.Domain创建一个Books的文件夹,并添加一个Book的基础类,如下图:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Volo.Abp.Domain.Entities.Auditing; namespace TestApp.BookStore.Books { public class Book :AuditedAggregateRoot{ public string Name { get; set; } public BookType Type { get; set; } public DateTime PublishDate { get; set; } public float Price { get; set; } } }
- ABP为实体提供了两个基本的基类:
AggregateRoot
和Entity
. Aggregate Root是领域驱动设计 概念之一. 可以视为直接查询和处理的根实体. Book
实体继承了AuditedAggregateRoot
,AuditedAggregateRoot
类在AggregateRoot
类的基础上添加了一些基础审计属性(例如CreationTime
,CreatorId
,LastModificationTime
等). ABP框架自动为你管理这些属性.Guid
是Book
实体的主键类型.
由于Book实体包含
枚举,所以在BookType
TestApp.BookStore.Domain.Shared
创建一个Books的文件夹,并添加一个BookType的基础类,如下图:
using System; using System.Collections.Generic; using System.Text; namespace TestApp.BookStore.Books { public enum BookType { Undefined, Adventure, Biography, Dystopia, Fantastic, Horror, Science, ScienceFiction, Poetry } }
二、将Book实体添加到DbContext中并映射到数据库表中
EF Core需要你将实体和 DbContext
建立关联.做法是在TestApp.BookStore.EntityFrameworkCore
项目的BookStoreDbContext
类
添加DbSet
属性.如下所示:
public DbSetBooks { get; set; }
OnModelCreating
方法,为Book
实体添加映射代码:
builder.Entity(b => { b.ToTable(BookStoreConsts.DbTablePrefix + "Books",BookStoreConsts.DbSchema); b.ConfigureByConvention(); b.Property(x => x.Name).IsRequired().HasMaxLength(128); });
BookStoreConsts
含有用于表的架构和表前缀的常量值. 使用它不是强制的,但建议在统一的地方控制表前缀.ConfigureByConvention()
方法优雅的配置/映射继承的属性,应对所有的实体使用它.
可在数据模型添加初始化数据方便学习ABP Framework功能,如下图(可忽略)
在 TestApp.BookStore.Domain项目下创建 IDataSeedContributor
的派生类
using System; using System.Threading.Tasks; using TestApp.BookStore.Books; using Volo.Abp.Data; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Repositories; namespace TestApp.BookStore { public class BookStoreDataSeederContributor : IDataSeedContributor, ITransientDependency { private readonly IRepository_bookRepository; public BookStoreDataSeederContributor(IRepository bookRepository) { _bookRepository = bookRepository; } public async Task SeedAsync(DataSeedContext context) { if (await _bookRepository.GetCountAsync() <= 0) { await _bookRepository.InsertAsync( new Book { Name = "1984", Type = BookType.Dystopia, PublishDate = new DateTime(1949, 6, 8), Price = 19.84f }, autoSave: true ); await _bookRepository.InsertAsync( new Book { Name = "The Hitchhiker's Guide to the Galaxy", Type = BookType.ScienceFiction, PublishDate = new DateTime(1995, 9, 27), Price = 42.0f }, autoSave: true ); } } } }
- 如果数据库中当前没有图书,则此代码使用
IRepository
(默认repository)将两本书插入数据库.
三、更新数据库
由于数据表结构和初始化数据调整,所以需要更新下数据库,将TestApp.BookStore.DbMigrator应用程序设为启动项目,
来更新数据库,运行完成会发现数据库book表会多两条默认数据,如下图:
.DbMigrator
是一个控制台使用程序,可以在开发和生产环境迁移数据库架构和初始化种子数据.
四、创建应用程序
前面也说道应用程序跟领域层类似都有两块组成:
TestApp.BookStore.Application.Contracts
包含你的DTO和应用服务接口.TestApp.BookStore.Application
包含你的应用服务实现.
创建一个应用程序服务,使用ABP Framework的 CrudAppService
基类来获取,创建,更新和删除书籍.
CrudAppService
基类需要定义实体的基本DTO.
在 TestApp.BookStore.Application.Contracts
项目中创建 Books
文件夹(命名空间),添加 BookDto
的DTO类:
using System; using System.Collections.Generic; using System.Text; using Volo.Abp.Application.Dtos; namespace TestApp.BookStore.Books { public class BookDto:AuditedEntityDto{ public string Name { get; set; } public BookType Type { get; set; } public DateTime PublishDate { get; set; } public float Price { get; set; } } }
- DTO类被用来在 表示层 和 应用层 传递数据.
- 为了在用户界面上展示书籍信息,
BookDto
被用来将书籍数据传递到表示层. BookDto
继承自AuditedEntityDto
.与上面定义的Book
实体一样具有一些审计属性.
在将书籍返回到表示层时,需要将Book
实体转换为BookDto
对象. AutoMapper库可以在定义了正确的映射时自动执行此转换. 启动模板配置了AutoMapper,因此你只需在TestApp.BookStore.Application
项目的BookStoreApplicationAutoMapperProfile
类中定义映射:
CreateMap();
在TestApp.BookStore.Application.Contracts
项目中创建 Books
文件夹(命名空间),并在其中添加名为 CreateUpdateBookDto
的DTO类:
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Text; namespace TestApp.BookStore.Books { public class CreateUpdateBookDto { [Required] [StringLength(128)] public string Name { get; set; } [Required] public BookType Type { get; set; } = BookType.Undefined; [Required] [DataType(DataType.Date)] public DateTime PublishDate { get; set; } = DateTime.Now; [Required] public float Price { get; set; } } }
- 这个DTO类被用于在创建或更新书籍的时候从用户界面获取图书信息.
- 它定义了数据注释特性(如
[Required]
)来定义属性的验证规则. DTO由ABP框架自动验证.
就像上面的BookDto
一样,创建一个从CreateUpdateBookDto
对象到Book
实体的映射,最终映射配置类如下:
CreateMap();
在TestApp.BookStore.Application.Contracts
项目中创建 Books
文件夹(命名空间),并在其中添加名为IBookAppService接口:
using System; using System.Collections.Generic; using System.Text; using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; namespace TestApp.BookStore.Books { public interface IBookAppService: ICrudAppService< //Defines CRUD methods BookDto, //Used to show books Guid, //Primary key of the book entity PagedAndSortedResultRequestDto, //Used for paging/sorting CreateUpdateBookDto> //Used to create/update a book { } }
- 框架定义应用程序服务的接口不是必需的. 但是,它被建议作为最佳实践.
ICrudAppService
定义了常见的CRUD方法:GetAsync
,GetListAsync
,CreateAsync
,UpdateAsync
和DeleteAsync
. 从这个接口扩展不是必需的,你可以从空的IApplicationService
接口继承并手动定义自己的方法(将在下一部分中完成).ICrudAppService
有一些变体, 你可以在每个方法中使用单独的DTO(例如使用不同的DTO进行创建和更新).
TestApp.BookStore.Application.Contracts项目新增文件内容如下图:
在TestApp.BookStore.Application
项目中创建 Books
文件夹(命名空间),并在其中添加名为 BookAppService
的类实现IBookAppService接口:using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; using Volo.Abp.Domain.Repositories; namespace TestApp.BookStore.Books { public class BookAppService : CrudAppService< Book, //The Book entity BookDto, //Used to show books Guid, //Primary key of the book entity PagedAndSortedResultRequestDto, //Used for paging/sorting CreateUpdateBookDto>, //Used to create/update a book IBookAppService { public BookAppService(IRepositoryrepositoty) : base(repositoty) { } } }
五、自动生成API Controllers
在典型的ASP.NET Core应用程序中,你创建API Controller以将应用程序服务公开为HTTP API端点. 这将允许浏览器或第三方客户端通过HTTP调用它们.
ABP可以自动按照约定将你的应用程序服务配置为MVC API控制器.
六、运行Swagger UI测试API
运行应用程序并在浏览器中输入https://localhost:XXXX/swagger/
(用你自己的端口替换XXXX)作为URL. 使用CTRL+F5
运行应用程序 (TestApp.BookStore.Web
)并使用浏览器访问https://localhost:/swagger/
可以尝试执行[GET] /api/app/book
API来获取书籍列表, 服务端会返回以下JSON结果:
到现在为止新增的Book模块基本完成,下片接着介绍如何操作Book的增删改查功能......