mirror of https://github.com/abpframework/abp.git
3 changed files with 217 additions and 0 deletions
@ -0,0 +1,217 @@ |
|||
# Many to Many Relationship with ABP and EF Core |
|||
|
|||
## Introduction |
|||
|
|||
In this article, we'll create a **BookStore** application like in [the ABP tutorial](https://docs.abp.io/en/abp/latest/Tutorials/Part-1?UI=MVC&DB=EF) and add an extra `Category` feature to demonstrate how we can manage the many-to-many relationship with ABP-based applications (by following DDD rules). |
|||
|
|||
You can see the ER Diagram of our application under below. This diagram will be helpful for us to demonstrate the relations between our entities. |
|||
|
|||
 |
|||
|
|||
When we've examined the ER Diagram, we can see the one-to-many relationship between **Author** and **Book** tables and also the many-to-many relationship (**BookCategory** table) between **Book** and **Category** tables. Cause, there can be more than one category on each book and vice-versa in our scenario. |
|||
|
|||
### Source Code |
|||
|
|||
You can find the source code of the application at https://github.com/EngincanV/ABP-Many-to-Many-Relationship-Demo . |
|||
## Creating the Solution |
|||
|
|||
In this article, I will create a new startup template with EF Core as a database provider and MVC for UI framework. |
|||
|
|||
* We can create a new startup template by using [ABP CLI](https://docs.abp.io/en/abp/latest/CLI): |
|||
|
|||
```bash |
|||
abp new BookStore -t app --version 5.0.0-beta.2 |
|||
``` |
|||
|
|||
* Our project boilerplate will be ready after the download is finished. Then, we can open the solution and starts to the development. |
|||
|
|||
## Starting the Development |
|||
|
|||
Let's start with creating our Domain Entities. |
|||
|
|||
### Step 1 - (Creating the Domain Entities) |
|||
|
|||
We can create a folder-structure under the `BookStore.Domain` project like in the below image. |
|||
|
|||
 |
|||
|
|||
Open the entity classes and add the following codes to each of these classes. |
|||
|
|||
* **Author.cs** |
|||
|
|||
```csharp |
|||
using System; |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Domain.Entities.Auditing; |
|||
|
|||
namespace BookStore.Authors |
|||
{ |
|||
public class Author : FullAuditedAggregateRoot<Guid> |
|||
{ |
|||
public string Name { get; private set; } |
|||
|
|||
public DateTime BirthDate { get; set; } |
|||
|
|||
public string ShortBio { get; set; } |
|||
|
|||
/* This constructor is for deserialization / ORM purpose */ |
|||
private Author() |
|||
{ |
|||
} |
|||
|
|||
public Author(Guid id, [NotNull] string name, DateTime birthDate, [CanBeNull] string shortBio = null) |
|||
: base(id) |
|||
{ |
|||
SetName(name); |
|||
BirthDate = birthDate; |
|||
ShortBio = shortBio; |
|||
} |
|||
|
|||
public void SetName([NotNull] string name) |
|||
{ |
|||
Name = Check.NotNullOrWhiteSpace( |
|||
name, |
|||
nameof(name), |
|||
maxLength: AuthorConsts.MaxNameLength |
|||
); |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
> We'll create the `AuthorConsts` class later in this step. |
|||
|
|||
* **Book.cs** |
|||
|
|||
```csharp |
|||
using System; |
|||
using Volo.Abp.Domain.Entities.Auditing; |
|||
|
|||
namespace BookStore.Books |
|||
{ |
|||
public class Book : FullAuditedAggregateRoot<Guid> |
|||
{ |
|||
public Guid AuthorId { get; set; } |
|||
|
|||
public string Name { get; set; } |
|||
|
|||
public BookType Type { get; set; } |
|||
|
|||
public DateTime PublishDate { get; set; } |
|||
|
|||
public float Price { get; set; } |
|||
|
|||
/* This constructor is for deserialization / ORM purpose */ |
|||
private Book() |
|||
{ |
|||
} |
|||
|
|||
public Book(Guid id, Guid authorId, string name, BookType type, DateTime publishDate, float price) |
|||
: base(id) |
|||
{ |
|||
AuthorId = authorId; |
|||
Name = name; |
|||
Type = type; |
|||
PublishDate = publishDate; |
|||
Price = price; |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
> We'll create the `BookType` enum later in this step. |
|||
|
|||
* **Category.cs** |
|||
|
|||
```csharp |
|||
using System; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Domain.Entities.Auditing; |
|||
|
|||
namespace BookStore.Categories |
|||
{ |
|||
public class Category : AuditedAggregateRoot<Guid> |
|||
{ |
|||
public string Name { get; private set; } |
|||
|
|||
public bool IsActive { get; set; } |
|||
|
|||
/* This constructor is for deserialization / ORM purpose */ |
|||
private Category() |
|||
{ |
|||
} |
|||
|
|||
public Category(Guid id, string name, bool isActive = true) : base(id) |
|||
{ |
|||
SetName(name); |
|||
IsActive = isActive; |
|||
} |
|||
|
|||
public Category SetName(string name) |
|||
{ |
|||
Name = Check.NotNullOrWhiteSpace(name, nameof(name), BookConsts.MaxNameLength); |
|||
return this; |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
> We'll create the `BookConsts` class later in this step. |
|||
|
|||
* **BookCategory.cs** |
|||
|
|||
```csharp |
|||
using System; |
|||
using Volo.Abp.Domain.Entities; |
|||
|
|||
namespace BookStore.Books |
|||
{ |
|||
public class BookCategory : Entity |
|||
{ |
|||
public Guid BookId { get; protected set; } |
|||
|
|||
public Guid CategoryId { get; protected set; } |
|||
|
|||
private BookCategory() |
|||
{ |
|||
} |
|||
|
|||
public BookCategory(Guid bookId, Guid categoryId) |
|||
{ |
|||
BookId = bookId; |
|||
CategoryId = categoryId; |
|||
} |
|||
|
|||
public override object[] GetKeys() |
|||
{ |
|||
return new object[] {BookId, CategoryId}; |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
Here, as you can notice we've defined the `BookCategory` as the **Join Table** for our many-to-many relationship and ensure the required properties (BookId and CategoryId) must be set to create this object. |
|||
|
|||
And also we've derived this class from `Entity` class and therefore we've had to override the **GetKeys** method of this class to define **Composite Key** |
|||
|
|||
> The composite key is composed of `BookId` and `CategoryId` in our case. |
|||
|
|||
> For more information about **Entities with Composite Keys**, you can read the relavant section from [Entites documentation](https://docs.abp.io/en/abp/latest/Entities#entities-with-composite-keys) |
|||
|
|||
|
|||
|
|||
* After defining our entities, now we can open the `BookStore.Domain.Shared` project and add the relevant constants and enums from there. |
|||
|
|||
* |
|||
|
|||
### Step 2 - (Database Integration) |
|||
|
|||
|
|||
--- |
|||
|
|||
* Create the Domain Entities ✓ |
|||
* Database Integration ✓ |
|||
* Db migration ✓ |
|||
* app services (category -> crud app service) |
|||
* ui |
|||
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 18 KiB |
Loading…
Reference in new issue