From 1fb29ca83c71d99e2425bf27d36da9b40ec90c99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 22 Jul 2020 00:05:11 +0300 Subject: [PATCH] Added the part 10. --- docs/en/Tutorials/Part-1.md | 6 +- docs/en/Tutorials/Part-10.md | 231 +++++++++++++++++++++++++++++++++++ docs/en/Tutorials/Part-2.md | 1 + docs/en/Tutorials/Part-3.md | 1 + docs/en/Tutorials/Part-4.md | 1 + docs/en/Tutorials/Part-5.md | 1 + docs/en/Tutorials/Part-6.md | 1 + docs/en/Tutorials/Part-7.md | 1 + docs/en/Tutorials/Part-8.md | 43 ++++--- docs/en/Tutorials/Part-9.md | 9 +- 10 files changed, 274 insertions(+), 21 deletions(-) create mode 100644 docs/en/Tutorials/Part-10.md diff --git a/docs/en/Tutorials/Part-1.md b/docs/en/Tutorials/Part-1.md index 3028e88931..f7a02725f0 100644 --- a/docs/en/Tutorials/Part-1.md +++ b/docs/en/Tutorials/Part-1.md @@ -41,6 +41,7 @@ This tutorial is organized as the following parts; - [Part 7: Authors: Database Integration](Part-7.md) - [Part 8: Authors: Application Layer](Part-8.md) - [Part 9: Authors: User Interface](Part-9.md) +- [Part 10: Book to Author Relation](Part-10.md) ### Download the Source Code @@ -231,11 +232,6 @@ namespace Acme.BookStore } public async Task SeedAsync(DataSeedContext context) - { - await CreateBooksAsync(); - } - - private async Task CreateBooksAsync() { if (await _bookRepository.GetCountAsync() > 0) { diff --git a/docs/en/Tutorials/Part-10.md b/docs/en/Tutorials/Part-10.md new file mode 100644 index 0000000000..da2ba59bcb --- /dev/null +++ b/docs/en/Tutorials/Part-10.md @@ -0,0 +1,231 @@ +# Web Application Development Tutorial - Part 10: Book to Author Relation +````json +//[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-1.md) +- [Part 2: The book list page](Part-2.md) +- [Part 3: Creating, updating and deleting books](Part-3.md) +- [Part 4: Integration tests](Part-4.md) +- [Part 5: Authorization](Part-5.md) +- [Part 6: Authors: Domain layer](Part-6.md) +- [Part 7: Authors: Database Integration](Part-7.md) +- [Part 8: Authors: Application Layer](Part-8.md) +- [Part 9: Authors: User Interface](Part-9.md) +- **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: + +* [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore) +* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb) + +## 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: + +````csharp +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 `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 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` part as shown below: + +````csharp +builder.Entity(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().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: + +````bash +Add-Migration "Added_AuthorId_To_Book" +```` + +This should create a new migration class with the following code in its `Up` method: + +````csharp +migrationBuilder.AddColumn( + 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 `AuthorId` field to the `AppBooks` table. +* Creates an index on the `AuthorId` field. +* Declares the foreign key to the `AppAuthors` table. + +{{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: + +````csharp +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 _bookRepository; + private readonly IAuthorRepository _authorRepository; + private readonly AuthorManager _authorManager; + + public BookStoreDataSeederContributor( + IRepository 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 \ No newline at end of file diff --git a/docs/en/Tutorials/Part-2.md b/docs/en/Tutorials/Part-2.md index d6ae9e33c9..8db6986ae6 100644 --- a/docs/en/Tutorials/Part-2.md +++ b/docs/en/Tutorials/Part-2.md @@ -41,6 +41,7 @@ This tutorial is organized as the following parts; - [Part 7: Authors: Database Integration](Part-7.md) - [Part 8: Authors: Application Layer](Part-8.md) - [Part 9: Authors: User Interface](Part-9.md) +- [Part 10: Book to Author Relation](Part-10.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-3.md b/docs/en/Tutorials/Part-3.md index d3dcc4ab2a..fc8e724597 100644 --- a/docs/en/Tutorials/Part-3.md +++ b/docs/en/Tutorials/Part-3.md @@ -41,6 +41,7 @@ This tutorial is organized as the following parts; - [Part 7: Authors: Database Integration](Part-7.md) - [Part 8: Authors: Application Layer](Part-8.md) - [Part 9: Authors: User Interface](Part-9.md) +- [Part 10: Book to Author Relation](Part-10.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-4.md b/docs/en/Tutorials/Part-4.md index 7c20975005..1f27e1f6a5 100644 --- a/docs/en/Tutorials/Part-4.md +++ b/docs/en/Tutorials/Part-4.md @@ -41,6 +41,7 @@ This tutorial is organized as the following parts; - [Part 7: Authors: Database Integration](Part-7.md) - [Part 8: Authors: Application Layer](Part-8.md) - [Part 9: Authors: User Interface](Part-9.md) +- [Part 10: Book to Author Relation](Part-10.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-5.md b/docs/en/Tutorials/Part-5.md index d18e22d7d8..6fc760f66a 100644 --- a/docs/en/Tutorials/Part-5.md +++ b/docs/en/Tutorials/Part-5.md @@ -41,6 +41,7 @@ This tutorial is organized as the following parts; - [Part 7: Authors: Database Integration](Part-7.md) - [Part 8: Authors: Application Layer](Part-8.md) - [Part 9: Authors: User Interface](Part-9.md) +- [Part 10: Book to Author Relation](Part-10.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-6.md b/docs/en/Tutorials/Part-6.md index 96c94a2f2e..6ee16bb1b4 100644 --- a/docs/en/Tutorials/Part-6.md +++ b/docs/en/Tutorials/Part-6.md @@ -41,6 +41,7 @@ This tutorial is organized as the following parts; - [Part 7: Authors: Database Integration](Part-7.md) - [Part 8: Authors: Application Layer](Part-8.md) - [Part 9: Authors: User Interface](Part-9.md) +- [Part 10: Book to Author Relation](Part-10.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-7.md b/docs/en/Tutorials/Part-7.md index 95bdf553b4..dcfe2453ad 100644 --- a/docs/en/Tutorials/Part-7.md +++ b/docs/en/Tutorials/Part-7.md @@ -41,6 +41,7 @@ This tutorial is organized as the following parts; - **Part 7: Authors: Database Integration (this part)** - [Part 8: Authors: Application Layer](Part-8.md) - [Part 9: Authors: User Interface](Part-9.md) +- [Part 10: Book to Author Relation](Part-10.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-8.md b/docs/en/Tutorials/Part-8.md index 59ec6d8b3d..89a8c58e01 100644 --- a/docs/en/Tutorials/Part-8.md +++ b/docs/en/Tutorials/Part-8.md @@ -41,6 +41,7 @@ This tutorial is organized as the following parts; - [Part 7: Authors: Database Integration](Part-7.md) - **Part 8: Author: Application Layer (this part)** - [Part 9: Authors: User Interface](Part-9.md) +- [Part 10: Book to Author Relation](Part-10.md) ### Download the Source Code @@ -406,7 +407,7 @@ CreateMap(); As just done for the books before, it would be good to have some initial author entities in the database. This will be good while running the application first time, but also it is very useful for the automated tests. -Open the `BookStoreDataSeederContributor` in the `Acme.BookStore.Domain` project and add a new `CreateAuthorsAsync` method as shown below: +Open the `BookStoreDataSeederContributor` in the `Acme.BookStore.Domain` project and change the file content with the code below: ````csharp using System; @@ -438,23 +439,18 @@ namespace Acme.BookStore public async Task SeedAsync(DataSeedContext context) { - await CreateAuthorsAsync(); // CALL the NEW METHOD - await CreateBooksAsync(); - } - - // ADDED a NEW METHOD - private async Task CreateAuthorsAsync() - { - if (await _authorRepository.GetCountAsync() > 0) + if (await _bookRepository.GetCountAsync() > 0) { return; } + // ADDED SEED DATA FOR AUTHORS + await _authorRepository.InsertAsync( await _authorManager.CreateAsync( "George Orwell", new DateTime(1903, 06, 25), - "Orwell produced literary criticism and poetry..." + "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)." ) ); @@ -462,14 +458,31 @@ namespace Acme.BookStore await _authorManager.CreateAsync( "Douglas Adams", new DateTime(1952, 03, 11), - "Douglas Adams was an English author, screenwriter..." + "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'." ) ); - } - private async Task CreateBooksAsync() - { - //...omitted the code + 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 + ); } } } diff --git a/docs/en/Tutorials/Part-9.md b/docs/en/Tutorials/Part-9.md index 7c6db34bb9..147de367c4 100644 --- a/docs/en/Tutorials/Part-9.md +++ b/docs/en/Tutorials/Part-9.md @@ -41,6 +41,7 @@ This tutorial is organized as the following parts; - [Part 7: Authors: Database Integration](Part-7.md) - [Part 8: Authors: Application Layer](Part-8.md) - **Part 9: Authors: User Interface (this part)** +- [Part 10: Book to Author Relation](Part-10.md) ### Download the Source Code @@ -258,6 +259,8 @@ As you see, the admin role has no *Author Management* permissions yet. Click to The page is fully working except *New author* and *Actions/Edit* since we haven't implemented them yet. +> **Tip**: If you run the `.DbMigrator` console application after defining a new permission, it automatically grants these new permissions to the admin role and you don't need to manually grant the permissions yourself. + ## Create Modal Create a new razor page, `CreateModal.cshtml` under the `Pages/Authors` folder of the `Acme.BookStore.Web` project and change the content as given below. @@ -508,4 +511,8 @@ That's all! You can run the application and try to edit an author. TODO: Angular UI is being prepared... -{{end}} \ No newline at end of file +{{end}} + +## The Next Part + +See the [next part](part-10.md) of this tutorial. \ No newline at end of file