7.9 KiB
Web Application Development Tutorial - Part 10: Book to Author Relation
//[doc-params]
{
"UI": ["MVC","NG"],
"DB": ["EF","Mongo"]
}
{{ if UI == "MVC" UI_Text="mvc" else if UI == "NG" UI_Text="angular" else UI_Text="?" end if DB == "EF" DB_Text="Entity Framework Core" else if DB == "Mongo" DB_Text="MongoDB" else DB_Text="?" end }}
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:
- {{DB_Text}} as the ORM provider.
- {{UI_Value}} 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 tutorials has multiple versions based on your UI and Database preferences. We've prepared two 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 Book and the Author.
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:
public Guid AuthorId { get; set; }
Database 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 AuthorIds 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 do it programmatically on data migration or seed phase.
- You can manually handle it on the database.
We prefer to drop the database (run the Drop-Database in the Package Manager Console) 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 scenarios.
{{if DB=="EF"}}
Update the EF Core Mapping
Open the BookStoreDbContextModelCreatingExtensions class under the EntityFrameworkCore folder of the Acme.BookStore.EntityFrameworkCore project and change the builder.Entity<Book> part as shown below:
builder.Entity<Book>(b =>
{
b.ToTable(BookStoreConsts.DbTablePrefix + "Books", BookStoreConsts.DbSchema);
b.ConfigureByConvention(); //auto configure for the base class props
b.Property(x => x.Name).IsRequired().HasMaxLength(128);
// ADD THE MAPPING FOR THE RELATION
b.HasOne<Author>().WithMany().HasForeignKey(x => x.AuthorId).IsRequired();
});
Add New EF Core Migration
Run the following command in the Package Manager Console (of the Visual Studio) to add a new database migration:
Add-Migration "Added_AuthorId_To_Book"
This should create a new migration class with the following code in its Up method:
migrationBuilder.AddColumn<Guid>(
name: "AuthorId",
table: "AppBooks",
nullable: false,
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"));
migrationBuilder.CreateIndex(
name: "IX_AppBooks_AuthorId",
table: "AppBooks",
column: "AuthorId");
migrationBuilder.AddForeignKey(
name: "FK_AppBooks_AppAuthors_AuthorId",
table: "AppBooks",
column: "AuthorId",
principalTable: "AppAuthors",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
- Adds an
AuthorIdfield to theAppBookstable. - Creates an index on the
AuthorIdfield. - Declares the foreign key to the
AppAuthorstable.
{{end}}
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:
using System;
using System.Threading.Tasks;
using Acme.BookStore.Authors;
using Acme.BookStore.Books;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
namespace Acme.BookStore
{
public class BookStoreDataSeederContributor
: IDataSeedContributor, ITransientDependency
{
private readonly IRepository<Book, Guid> _bookRepository;
private readonly IAuthorRepository _authorRepository;
private readonly AuthorManager _authorManager;
public BookStoreDataSeederContributor(
IRepository<Book, Guid> bookRepository,
IAuthorRepository authorRepository,
AuthorManager authorManager)
{
_bookRepository = bookRepository;
_authorRepository = authorRepository;
_authorManager = authorManager;
}
public async Task SeedAsync(DataSeedContext context)
{
if (await _bookRepository.GetCountAsync() > 0)
{
return;
}
var orwell = await _authorRepository.InsertAsync(
await _authorManager.CreateAsync(
"George Orwell",
new DateTime(1903, 06, 25),
"Orwell produced literary criticism and poetry, fiction and polemical journalism; and is best known for the allegorical novella Animal Farm (1945) and the dystopian novel Nineteen Eighty-Four (1949)."
)
);
var douglas = await _authorRepository.InsertAsync(
await _authorManager.CreateAsync(
"Douglas Adams",
new DateTime(1952, 03, 11),
"Douglas Adams was an English author, screenwriter, essayist, humorist, satirist and dramatist. Adams was an advocate for environmentalism and conservation, a lover of fast cars, technological innovation and the Apple Macintosh, and a self-proclaimed 'radical atheist'."
)
);
await _bookRepository.InsertAsync(
new Book
{
AuthorId = orwell.Id, // SET THE AUTHOR
Name = "1984",
Type = BookType.Dystopia,
PublishDate = new DateTime(1949, 6, 8),
Price = 19.84f
},
autoSave: true
);
await _bookRepository.InsertAsync(
new Book
{
AuthorId = douglas.Id, // SET THE AUTHOR
Name = "The Hitchhiker's Guide to the Galaxy",
Type = BookType.ScienceFiction,
PublishDate = new DateTime(1995, 9, 27),
Price = 42.0f
},
autoSave: true
);
}
}
}
The only change is that we set the AuthorId properties of the Book entities.
{{if DB=="EF"}}
You can now run the .DbMigrator console application to migrate the database schema and seed the initial data.
{{else if DB="Mongo"}}
You can now run the .DbMigrator console application to seed the initial data.
{{end}}
Application Layer
TODO