Web Application Development Tutorial - Part 10: Book to Author Relation
About This Tutorial
In this tutorial series, you will build an ABP based web application named Acme.BookStore
. This application is used to manage a list of books and their authors. It is developed using the following technologies:
- MongoDB as the ORM provider.
- Blazor WebAssembly as the UI Framework.
This tutorial is organized as the following parts;
- Part 1: Creating the server side
- Part 2: The book list page
- Part 3: Creating, updating and deleting books
- Part 4: Integration tests
- Part 5: Authorization
- Part 6: Authors: Domain layer
- Part 7: Authors: Database Integration
- Part 8: Authors: Application Layer
- Part 9: Authors: User Interface
- Part 10: Book to Author Relation (this part)
Download the Source Code
This tutorial has multiple versions based on your UI and Database preferences. We've prepared a few combinations of the source code to be downloaded:
Introduction
We have created Book
and Author
functionalities for the book store application. However, currently there is no relation between these entities.
In this tutorial, we will establish a 1 to N relation between the Author
and the Book
entities.
Add Relation to The Book Entity
Open the Books/Book.cs
in the Acme.BookStore.Domain
project and add the following property to the Book
entity:
Database & Data Migration
Added a new, required AuthorId
property to the Book
entity. But, what about the existing books on the database? They currently don't have AuthorId
s and this will be a problem when we try to run the application.
This is a typical migration problem and the decision depends on your case;
- If you haven't published your application to the production yet, you can just delete existing books in the database, or you can even delete the entire database in your development environment.
- You can update the existing data programmatically on data migration or seed phase.
- You can manually handle it on the database.
We prefer to delete the database since this is just an example project and data loss is not important. Since this topic is not related to the ABP Framework, we don't go deeper for all the scenarios.
Change the Data Seeder
Since the AuthorId
is a required property of the Book
entity, current data seeder code can not work. Open the BookStoreDataSeederContributor
in the Acme.BookStore.Domain
project and change as the following:
The only change is that we set the AuthorId
properties of the Book
entities.
Delete existing books or delete the database before executing the
DbMigrator
. See the Database & Data Migration section above for more info.
You can now run the .DbMigrator
console application to seed the initial data.
Application Layer
We will change the BookAppService
to support the Author relation.
Data Transfer Objects
Let's begin from the DTOs.
BookDto
Open the BookDto
class in the Books
folder of the Acme.BookStore.Application.Contracts
project and add the following properties:
The final BookDto
class should be following:
CreateUpdateBookDto
Open the CreateUpdateBookDto
class in the Books
folder of the Acme.BookStore.Application.Contracts
project and add an AuthorId
property as shown:
AuthorLookupDto
Create a new class, AuthorLookupDto
, inside the Books
folder of the Acme.BookStore.Application.Contracts
project:
This will be used in a new method that will be added to the IBookAppService
.
IBookAppService
Open the IBookAppService
interface in the Books
folder of the Acme.BookStore.Application.Contracts
project and add a new method, named GetAuthorLookupAsync
, as shown below:
This new method will be used from the UI to get a list of authors and fill a dropdown list to select the author of a book.
BookAppService
Open the BookAppService
interface in the Books
folder of the Acme.BookStore.Application
project and replace the file content with the following code:
Let's see the changes we've done:
- Added
[Authorize(BookStorePermissions.Books.Default)]
to authorize the methods we've newly added/overrode (remember, authorize attribute is valid for all the methods of the class when it is declared for a class). - Injected
IAuthorRepository
to query from the authors. - Overrode the
GetAsync
method of the baseCrudAppService
, which returns a singleBookDto
object with the givenid
. - Overrode the
GetListAsync
method of the baseCrudAppService
, which returns a list of books. This code separately queries the authors from database and sets the name of the authors in the application code. Instead, you could create a custom repository method and perform a join query or take the power of the MongoDB API to get the books and their authors in a single query, which would be more performant. - Created a new method:
GetAuthorLookupAsync
. This simple gets all the authors. The UI uses this method to fill a dropdown list and select and author while creating/editing books.
Object to Object Mapping Configuration
Introduced the AuthorLookupDto
class and used object mapping inside the GetAuthorLookupAsync
method. So, we need to add a new mapping definition inside the BookStoreApplicationAutoMapperProfile.cs
file of the Acme.BookStore.Application
project:
Unit Tests
Some of the unit tests will fail since we made some changed on the AuthorAppService
. Open the BookAppService_Tests
in the Books
folder of the Acme.BookStore.Application.Tests
project and change the content as the following:
- Changed the assertion condition in the
Should_Get_List_Of_Books
fromb => b.Name == "1984"
tob => b.Name == "1984" && b.AuthorName == "George Orwell"
to check if the author name was filled. - Changed the
Should_Create_A_Valid_Book
method to set theAuthorId
while creating a new book, since it is required anymore.
The User Interface
The Book List
It is very easy to show the Author Name in the book list. Open the /Pages/Books.razor
file in the Acme.BookStore.Blazor
project and add the following DataGridColumn
definition just after the Name
(book name) column:
When you run the application, you can see the Author column on the table:
Create Book Modal
Add the following field to the @code
section of the Books.razor
file:
Override the OnInitializedAsync
method and adding the following code:
- It is essential to call the
base.OnInitializedAsync()
sinceAbpCrudPageBase
has some initialization code to be executed.
The final @code
block should be the following:
Finally, add the following Field
definition into the ModalBody
of the Create modal, as the first item, before the Name
field:
This requires to add a new localization key to the en.json
file:
You can run the application to see the Author Selection while creating a new book:
Edit Book Modal
Add the following Field
definition into the ModalBody
of the Edit modal, as the first item, before the Name
field:
That's all. We are reusing the authorList
defined for the Create modal.