Added the part 10.

pull/4864/head
Halil İbrahim Kalkan 5 years ago
parent 56a653db10
commit 1fb29ca83c

@ -41,6 +41,7 @@ This tutorial is organized as the following parts;
- [Part 7: Authors: Database Integration](Part-7.md) - [Part 7: Authors: Database Integration](Part-7.md)
- [Part 8: Authors: Application Layer](Part-8.md) - [Part 8: Authors: Application Layer](Part-8.md)
- [Part 9: Authors: User Interface](Part-9.md) - [Part 9: Authors: User Interface](Part-9.md)
- [Part 10: Book to Author Relation](Part-10.md)
### Download the Source Code ### Download the Source Code
@ -231,11 +232,6 @@ namespace Acme.BookStore
} }
public async Task SeedAsync(DataSeedContext context) public async Task SeedAsync(DataSeedContext context)
{
await CreateBooksAsync();
}
private async Task CreateBooksAsync()
{ {
if (await _bookRepository.GetCountAsync() > 0) if (await _bookRepository.GetCountAsync() > 0)
{ {

@ -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<Book>` part as shown below:
````csharp
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:
````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<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 `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<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

@ -41,6 +41,7 @@ This tutorial is organized as the following parts;
- [Part 7: Authors: Database Integration](Part-7.md) - [Part 7: Authors: Database Integration](Part-7.md)
- [Part 8: Authors: Application Layer](Part-8.md) - [Part 8: Authors: Application Layer](Part-8.md)
- [Part 9: Authors: User Interface](Part-9.md) - [Part 9: Authors: User Interface](Part-9.md)
- [Part 10: Book to Author Relation](Part-10.md)
### Download the Source Code ### Download the Source Code

@ -41,6 +41,7 @@ This tutorial is organized as the following parts;
- [Part 7: Authors: Database Integration](Part-7.md) - [Part 7: Authors: Database Integration](Part-7.md)
- [Part 8: Authors: Application Layer](Part-8.md) - [Part 8: Authors: Application Layer](Part-8.md)
- [Part 9: Authors: User Interface](Part-9.md) - [Part 9: Authors: User Interface](Part-9.md)
- [Part 10: Book to Author Relation](Part-10.md)
### Download the Source Code ### Download the Source Code

@ -41,6 +41,7 @@ This tutorial is organized as the following parts;
- [Part 7: Authors: Database Integration](Part-7.md) - [Part 7: Authors: Database Integration](Part-7.md)
- [Part 8: Authors: Application Layer](Part-8.md) - [Part 8: Authors: Application Layer](Part-8.md)
- [Part 9: Authors: User Interface](Part-9.md) - [Part 9: Authors: User Interface](Part-9.md)
- [Part 10: Book to Author Relation](Part-10.md)
### Download the Source Code ### Download the Source Code

@ -41,6 +41,7 @@ This tutorial is organized as the following parts;
- [Part 7: Authors: Database Integration](Part-7.md) - [Part 7: Authors: Database Integration](Part-7.md)
- [Part 8: Authors: Application Layer](Part-8.md) - [Part 8: Authors: Application Layer](Part-8.md)
- [Part 9: Authors: User Interface](Part-9.md) - [Part 9: Authors: User Interface](Part-9.md)
- [Part 10: Book to Author Relation](Part-10.md)
### Download the Source Code ### Download the Source Code

@ -41,6 +41,7 @@ This tutorial is organized as the following parts;
- [Part 7: Authors: Database Integration](Part-7.md) - [Part 7: Authors: Database Integration](Part-7.md)
- [Part 8: Authors: Application Layer](Part-8.md) - [Part 8: Authors: Application Layer](Part-8.md)
- [Part 9: Authors: User Interface](Part-9.md) - [Part 9: Authors: User Interface](Part-9.md)
- [Part 10: Book to Author Relation](Part-10.md)
### Download the Source Code ### Download the Source Code

@ -41,6 +41,7 @@ This tutorial is organized as the following parts;
- **Part 7: Authors: Database Integration (this part)** - **Part 7: Authors: Database Integration (this part)**
- [Part 8: Authors: Application Layer](Part-8.md) - [Part 8: Authors: Application Layer](Part-8.md)
- [Part 9: Authors: User Interface](Part-9.md) - [Part 9: Authors: User Interface](Part-9.md)
- [Part 10: Book to Author Relation](Part-10.md)
### Download the Source Code ### Download the Source Code

@ -41,6 +41,7 @@ This tutorial is organized as the following parts;
- [Part 7: Authors: Database Integration](Part-7.md) - [Part 7: Authors: Database Integration](Part-7.md)
- **Part 8: Author: Application Layer (this part)** - **Part 8: Author: Application Layer (this part)**
- [Part 9: Authors: User Interface](Part-9.md) - [Part 9: Authors: User Interface](Part-9.md)
- [Part 10: Book to Author Relation](Part-10.md)
### Download the Source Code ### Download the Source Code
@ -406,7 +407,7 @@ CreateMap<Author, AuthorDto>();
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. 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 ````csharp
using System; using System;
@ -438,23 +439,18 @@ namespace Acme.BookStore
public async Task SeedAsync(DataSeedContext context) public async Task SeedAsync(DataSeedContext context)
{ {
await CreateAuthorsAsync(); // CALL the NEW METHOD if (await _bookRepository.GetCountAsync() > 0)
await CreateBooksAsync();
}
// ADDED a NEW METHOD
private async Task CreateAuthorsAsync()
{
if (await _authorRepository.GetCountAsync() > 0)
{ {
return; return;
} }
// ADDED SEED DATA FOR AUTHORS
await _authorRepository.InsertAsync( await _authorRepository.InsertAsync(
await _authorManager.CreateAsync( await _authorManager.CreateAsync(
"George Orwell", "George Orwell",
new DateTime(1903, 06, 25), 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( await _authorManager.CreateAsync(
"Douglas Adams", "Douglas Adams",
new DateTime(1952, 03, 11), 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() await _bookRepository.InsertAsync(
{ new Book
//...omitted the code {
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
);
} }
} }
} }

@ -41,6 +41,7 @@ This tutorial is organized as the following parts;
- [Part 7: Authors: Database Integration](Part-7.md) - [Part 7: Authors: Database Integration](Part-7.md)
- [Part 8: Authors: Application Layer](Part-8.md) - [Part 8: Authors: Application Layer](Part-8.md)
- **Part 9: Authors: User Interface (this part)** - **Part 9: Authors: User Interface (this part)**
- [Part 10: Book to Author Relation](Part-10.md)
### Download the Source Code ### 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. 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 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. 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... TODO: Angular UI is being prepared...
{{end}} {{end}}
## The Next Part
See the [next part](part-10.md) of this tutorial.
Loading…
Cancel
Save