ABP 构建Rest服务实验手册(二)

上一篇文章 中,我们使用 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(); //auto configure for the base class props
b.Property(x => x.Name).IsRequired().HasMaxLength(128);
});

更新数据库结构

修改完以上的代码后,我们需要修改对应的数据库结构,加入新增的,用于存储图书信息的表。 执行:

  1. 设置 Acme.BookStoreService.DbMigrator 为启动项目

  2. 设置 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, //Book实体的主键
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()
{
/* You can configure your AutoMapper mapping configuration here.
* Alternatively, you can split your mapping configurations
* into multiple profile classes for a better organization. */

CreateMap<Book, BookDto>();
CreateMap<CreateUpdateBookDto, Book>();

}
}
}

在 ABP 中,应用服务层中所说的服务并不是 WEB API, 而是存业务的操作,与具体的展现形式,或是存储形式无关

因为 ABP 会自动根据应用服务层中的接口生成 WEB API, 所以在本例中我们不需要单独为 WEB API 进行编程。

现在,可以设置 Acme.BookStoreService.HttpApi.Host 为启动项目,然后运行项目了。

本文标题:ABP 构建Rest服务实验手册(二)

文章作者:Morning Star

发布时间:2020年08月20日 - 16:08

最后更新:2021年04月16日 - 15:04

原始链接:https://www.mls-tech.info/dotnet/abp-practice-manual-2/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。