mirror of https://github.com/abpframework/abp.git
10 changed files with 274 additions and 21 deletions
@ -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 |
|||
Loading…
Reference in new issue