Web 应用程序开发教程(使用 ABP Suite)- 第 4 部分:Book 与 Author 的关系
在之前的几个部分中,你为书店应用程序创建了 Book 和 Author 实体(并为所有功能生成了代码)。然而,目前这些实体之间还没有任何关联。
在这一部分中,你将建立 Book 和 Author 实体之间的一对多关系。
使用 ABP Suite 建立关系
ABP Suite 允许建立一对多和多对多关系。
在本教程中,你只将在 Book 和 Author 实体之间建立一对多关系。使用 ABP Suite 建立关系非常简单。你只需要导航到导航属性选项卡,并为导航属性(1-n)或导航集合(n-n)关系提供元数据。
创建 Book 到 Author 的关系
请在 ABP Studio 的解决方案运行器面板中停止应用程序,因为 ABP Suite 会对解决方案进行更改,并且在某些步骤中可能需要构建解决方案,而正在运行的解决方案会阻止构建。
要在Book和Author实体之间建立一对多关系,请从CRUD 页面生成页面右上角的实体选择框中选择 Book 实体:
然后,你可以打开导航属性选项卡,并点击**添加导航属性(1-n)**按钮。之后,将会打开一个导航属性模型,你可以按照下图所示填写字段:
具体细节如下:
- 选择实体为
Author。(ABP Suite 将通过此配置在Book和Author实体之间建立一对多关系) - 设置属性名称为AuthorId,它将在数据库中设置为外键约束,所有相关的数据库配置都将由 ABP Suite 完成。
- 选择显示属性为Name,这将用于在下拉组件中为书籍设置作者,并且也会显示在Books页面的数据表中。
- 此外,将关系设置为必需,并设置为可筛选,以便可以根据作者筛选书籍。
注意:在代码生成之前,你应该删除数据库中的所有现有书籍(如果有的话)。因为,将为books表添加一个新的外键,如果表中存在任何记录,则新的迁移将无法应用到数据库,你可能需要手动更新数据库。由于软删除的存在,从 UI 删除现有记录是不够的,因此你需要手动删除记录。
指定元数据后,你可以点击确定按钮关闭模态框。然后,点击保存并生成按钮开始代码生成过程。ABP Suite 将在实体之间建立一对多关系,并自动生成所有必要的代码:
完成此过程需要一些时间。过程完成后,你将看到成功消息,可以点击确定按钮,然后通过在解决方案运行器面板中点击启动按钮(或者直接点击运行图标)来运行应用程序:
应用程序启动后,你可以右键单击并在应用程序上选择浏览,在 ABP Studio 预集成的浏览器中打开它。你可以先创建一个作者,然后创建一个带有该作者的书籍进行测试:
同时注意,在高级筛选部分,有一个作者下拉菜单,你可以用它来按作者筛选书籍(记得你在定义导航属性时设置了可筛选,多亏了这一点,ABP Suite 相应地生成了代码):
单元测试和集成测试
既然你已经完成了书店应用程序,现在我们可以检查生成的测试并运行它们,看看是否全部通过。
解决方案中有几个测试项目(根据你选择的 UI 和 Database 略有不同):
ABP Suite 为 Book 和 Author 实体生成了单元测试和集成测试。如果在你的 IDE 中打开测试资源管理器,你将看到生成了以下测试:
ABP Suite 为生成的代码生成了存储库实现和应用服务实现的测试,如果你在创建实体时启用了创建单元测试和集成测试选项。由于你在之前的部分已经这样做了,它为实体生成了所有必需的测试。
让我们检查其中一个生成的测试类。打开 BooksAppServiceTests(位于 test/Acme.BookStore.Application.Tests/Books/BookApplicationTests.cs 下)并查看 CreateAsync 方法:
[Fact]
public async Task CreateAsync()
{
// 准备
var input = new BookCreateDto
{
Name = "6c3d1eda8bf04852b7bd5dfdbbd93224b252478c2e474d4c8faf24fa6b182168ca830d4f80e64e4a8e363f33e151d1d34a04be4709274c7fbf2214f9bb3a16c3",
Type = default,
PublishDate = new DateTime(2006, 8, 21),
Price = 754882891,
AuthorId = Guid.Parse("602460f6-df6e-456a-89d9-8c5870dfc583")
};
// 执行
var serviceResult = await _booksAppService.CreateAsync(input);
// 断言
var result = await _bookRepository.FindAsync(c => c.Id == serviceResult.Id);
result.ShouldNotBe(null);
result.Name.ShouldBe("6c3d1eda8bf04852b7bd5dfdbbd93224b252478c2e474d4c8faf24fa6b182168ca830d4f80e64e4a8e363f33e151d1d34a04be4709274c7fbf2214f9bb3a16c3");
result.Type.ShouldBe(default);
result.PublishDate.ShouldBe(new DateTime(2006, 8, 21));
result.Price.ShouldBe(754882891);
}
ABP Suite:
- 创建
BookCreateDto输入 DTO 对象,并使用虚拟数据填充其值以模拟创建一本书, - 然后,调用
IBooksAppService.CreateAsync方法来创建书籍, - 最后,断言返回的结果是否符合预期。
注意,BookCreateDto 对象中也设置了 AuthorId。此时,你可能会问自己,我之前并没有创建那个 ID 的作者,难道不应该抛出异常吗?
不会,它不会抛出异常,因为 ABP Suite 也为测试生成了实体的简单虚拟数据!你可以在 Acme.BookStore.Domain.Tests 项目下看到测试数据种子贡献者:
以下是 AuthorsDataSeedContributor.SeedAsync 方法的内容:
public async Task SeedAsync(DataSeedContext context)
{
if (IsSeeded)
{
return;
}
await _authorRepository.InsertAsync(new Author
(
id: Guid.Parse("602460f6-df6e-456a-89d9-8c5870dfc583"),
name: "d7bbb3bff0d54ad799477298c4572e9c05fd1175ab21416da17d0001e2b697cd7fef99fdb4414f26a05789667a97442bd65865510ba34c3599e874ccf08b45e4",
birthDate: new DateTime(2010, 2, 11),
shortBio: "3c2ff43c18e34d7b9ad3f1b9c444cbb000f90808d3774cb6b7702b957f472d74048597f93df744f6a6fdf507be428e016edec982f1174e09b124982cbc40156290ce6bc9fd7b49b4972741956cc847891cb55ad0942f4534b90aa0561d3e0c200340b613c7ad40c38b4b2f2c39298169a853473faed34341a130b31e1eb57e92"
));
await _authorRepository.InsertAsync(new Author
(
id: Guid.Parse("6ea5a6b2-919e-4334-9728-13f4872e5e0e"),
name: "fd332fb58f184716962b08fbaa92f1c3e0963d843ba34c82bb5409517f60da3727c43b05e8d4490f996c5d19265962e53a69ed5e3e144509aad1441e37ce5081",
birthDate: new DateTime(2010, 6, 10),
shortBio: "b7808946c46c42e3935c4d8203d82973cfb98c5d81644f1da4ce1e643767849e23e0eb12a92f48be8f7eec0c07aefa043721fdd3fea542cfa644d2b7d428dc8842647180ef8a47139e097f6674c4f0d86c46765c406042a2a858865cb112ecd78d9ef6f5843e444994641f924a38a2d24ee4e212d41444888d3c0861af0cf9dd"
));
await _unitOfWorkManager!.Current!.SaveChangesAsync();
IsSeeded = true;
}
由于 ABP Suite 为每个实体生成了测试数据种子贡献者,你在测试服务时就有了初始数据。同样,正如你会注意到的,此示例中的 ID(602460f6-df6e-456a-89d9-8c5870dfc583)与 BooksAppServiceTests.CreateAsync 方法中的 authorId 字段相同。
让我们执行所有测试,并查看结果:
总结
到目前为止,你已经在不需要编写任何一行代码的情况下,为书店应用程序创建了所有功能。ABP Suite 生成了实体、应用服务、UI 组件、单元测试和集成测试等等……
在下一部分中,你将编写一些代码,并通过在指定的钩子点中编写代码来修改 ABP Suite 生成的代码。多亏了ABP Suite 的自定义代码支持,在下次生成时,我们的自定义代码不会被覆盖,并将得以保留。
抠丁客












