mirror of https://github.com/abpframework/abp
parent
56a653db10
commit
1fb29ca83c
@ -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