From 829809bacbe4df10e220071b7072498b1f73bd9f Mon Sep 17 00:00:00 2001 From: Engincan VESKE Date: Wed, 3 Nov 2021 11:34:40 +0300 Subject: [PATCH] Update the "many-to-many relationship" article --- .../POST.md | 452 +++++++++++++++++- .../domain-shared-file-structure.png | Bin 0 -> 24969 bytes 2 files changed, 431 insertions(+), 21 deletions(-) create mode 100644 docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/domain-shared-file-structure.png diff --git a/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/POST.md b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/POST.md index 97fbbc0ba3..09240c48c6 100644 --- a/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/POST.md +++ b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/POST.md @@ -4,11 +4,11 @@ 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. +You can see the ER Diagram of our application below. This diagram will be helpful for us to demonstrate the relations between our entities. ![ER-Diagram](./er-diagram.png) -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. +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. There can be more than one category on each book and vice-versa in our scenario. ### Source Code @@ -30,7 +30,7 @@ In this article, I will create a new startup template with EF Core as a database 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. +* Our project boilerplate will be ready after the download is finished. Then, we can open the solution and starts the development. ## Starting the Development @@ -177,20 +177,18 @@ namespace BookStore.Books } ``` -* In our scenerio, a book can have more than one category and a category can have more -than one book so it's obvious to we need to establish many-to-many relationship between them. +* In our scenario, a book can have more than one category and a category can have more than one book so we need to create a many-to-many relationship between them. -* For achieve this we will create an **join entity** named `BookCategory`, and this class -will simply have variables named `BookId` and `CategoryId`. +* For achieving this, we will create a **join entity** named `BookCategory`, and this class will simply have variables named `BookId` and `CategoryId`. -* To manage this **join entity**, we can add it as sub-collection to the **Book** entity, as we do above. We add this sub-collection -to **Book** class instead of **Category** class, because a book can have tens or mostly hundreds of category in it but in other -view a category can have more than hundred (or even way much) book inside of it. +* To manage this **join entity**, we can add it as a sub-collection to the **Book** entity, as we do above. We add this sub-collection +to **Book** class instead of **Category** class, because a book can have tens (or mostly hundreds) of categories but on the other perspective a category can have more than a hundred (or even way much) books inside of it. -* It is a significant performance problem to load thousands of items whenever you query a category. Therefore it's make much sense to add that sub-collection to `Book` entity. (Don't forget: **An aggregate (with the root entity and sub-collections) should -be serializable and transferrable on the wire as a single unit.**) +* It is a significant performance problem to load thousands of items whenever you query a category. Therefore it makes much more sense to add that sub-collection to the `Book` entity. (Don't forget: **An aggregate (with the root entity and sub-collections) should be serializable and transferrable on the wire as a single unit.**) -* Notice that, `BookCategory` is not an **Aggregate Root** so we are not violating the one of the base rule about Aggregate Root (Rule: "Reference Other Aggregates Only by ID"). +* Notice that, `BookCategory` is not an **Aggregate Root** so we are not violating one of the base rules about Aggregate Root (Rule: "Reference Other Aggregates Only by ID"). + +* If we examine the methods in the `Book` class (such as **RemoveAllCategories**, **RemoveAllCategoriesExceptGivenIds** and **AddCategory**) we will manage our sub-collection `Categories` (**BookCategory** - join table/entity) through them. (Adds or removes categories for books) > We'll create the `BookCategory` and `BookConsts` classes later in this step. @@ -227,15 +225,160 @@ namespace BookStore.Books } ``` -* Here, as you can notice we've defined the `BookCategory` as the **Join Table/Entity** for our many-to-many relationship and ensure the required properties (BookId and CategoryId) must be set to create this object. +* Here, as you can notice we've defined the `BookCategory` as the **Join Table/Entity** for our many-to-many relationship and ensure the required properties (BookId and CategoryId) must be set in the constructor method of this class 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**. +* And also we've derived this class from the `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. And they are unique with together. +> The composite key is composed of `BookId` and `CategoryId` in our case. And they are unique together. > 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) -//TODO: add BookManager class later? +* **BookManager.cs** + +```csharp +using System; +using System.Linq; +using System.Threading.Tasks; +using BookStore.Categories; +using JetBrains.Annotations; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Domain.Services; + +namespace BookStore.Books +{ + public class BookManager : DomainService + { + private readonly IBookRepository _bookRepository; + private readonly IRepository _categoryRepository; + + public BookManager(IBookRepository bookRepository, IRepository categoryRepository) + { + _bookRepository = bookRepository; + _categoryRepository = categoryRepository; + } + + public async Task CreateAsync(Guid authorId, string name, DateTime publishDate, float price, [CanBeNull]string[] categoryNames) + { + var book = new Book(GuidGenerator.Create(), authorId, name, publishDate, price); + + await SetCategoriesAsync(book, categoryNames); + + await _bookRepository.InsertAsync(book); + } + + public async Task UpdateAsync( + Book book, + Guid authorId, + string name, + DateTime publishDate, + float price, + [CanBeNull] string[] categoryNames + ) + { + book.AuthorId = authorId; + book.SetName(name); + book.PublishDate = publishDate; + book.Price = price; + + await SetCategoriesAsync(book, categoryNames); + + await _bookRepository.UpdateAsync(book); + } + + private async Task SetCategoriesAsync(Book book, [CanBeNull] string[] categoryNames) + { + if (categoryNames == null || !categoryNames.Any()) + { + book.RemoveAllCategories(); + return; + } + + var query = (await _categoryRepository.GetQueryableAsync()) + .Where(x => categoryNames.Contains(x.Name)) + .Select(x => x.Id) + .Distinct(); + + var categoryIds = await AsyncExecuter.ToListAsync(query); + if (!categoryIds.Any()) + { + return; + } + + book.RemoveAllCategoriesExceptGivenIds(categoryIds); + + foreach (var categoryId in categoryIds) + { + book.AddCategory(categoryId); + } + } + } +} +``` + +* If we examine the codes in the `BookManager` class, we can see that we've managed the `BookCategory` class (our join table/entity) +by using some methods that we've defined in the `Book` class such as **RemoveAllCategories**, **RemoveAllCategoriesExceptGivenIds** and **AddCategory**. + +* These methods basically add or remove categories related to the book by conditions. + +* In the `CreateAsync` method, if the category names are specified we are retrieving their ids from the database and by using the **AddCategory** method that we've defined in the `Book` class, we're adding them. + +* In the `UpdateAsync` method, the same logic is also valid. But in this case, the user could want to remove some categories from books, so if the user sends us an empty **categoryNames** array, we remove all categories from the book he wants to update. If the user sends us some category names, we remove the excluded ones and add the new ones according to **categoryNames** array. + +* **BookWithDetails.cs** + +```csharp +using System; +using Volo.Abp.Auditing; + +namespace BookStore.Books +{ + public class BookWithDetails : IHasCreationTime + { + public Guid Id { get; set; } + + public string Name { get; set; } + + public DateTime PublishDate { get; set; } + + public float Price { get; set; } + + public string AuthorName { get; set; } + + public string[] CategoryNames { get; set; } + + public DateTime CreationTime { get; set; } + } +} +``` + +* We will use this class to retrieve books with their sub-categories and author names. + +* **IBookRepository.cs** + +```csharp +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; + +namespace BookStore.Books +{ + public interface IBookRepository : IRepository + { + Task> GetListAsync( + string sorting, + int skipCount, + int maxResultCount, + CancellationToken cancellationToken = default + ); + + Task GetAsync(Guid id, CancellationToken cancellationToken = default); + } +} +``` + +* We need to create two methods named **GetListAsync** and **GetAsync** and specify their return type as `BookWithDetails`. So by implementing these methods, we will return the book/books by their details (author name and categories). * **Category.cs** @@ -269,10 +412,277 @@ namespace BookStore.Categories } ``` -### Step 2 - (Database Integration) +### Step 2 - (Define Consts) + +We can create a folder-structure under the `BookStore.Domain.Shared` project like in the below image. + +![Domain Shared File Structure](./domain-shared-file-structure.png) + +* **AuthorConsts.cs** + +```csharp +namespace BookStore.Authors +{ + public class AuthorConsts + { + public const int MaxNameLength = 128; + + public const int MaxShortBioLength = 256; + } +} +``` + +* **BookConsts.cs** + +```csharp +namespace BookStore.Books +{ + public class BookConsts + { + public const int MaxNameLength = 128; + } +} +``` + +* **CategoryConsts.cs** + +```csharp +namespace BookStore.Categories +{ + public class CategoryConsts + { + public const int MaxNameLength = 64; + } +} +``` + +* In these classes, we've defined max text length for our entity properties that we will use in the **Database Integration** section to specify limits for our properties. (E.g. varchar(128) for BookName) + +### Step 3 - (Database Integration) + +* After defining our entities, we can configure them for the database integration. +Open the `BookStoreDbContext` class in the `BookStore.EntityFrameworkCore` project and update with the following code blocks. + +```csharp +namespace BookStore.EntityFrameworkCore +{ + [ReplaceDbContext(typeof(IIdentityDbContext))] + [ReplaceDbContext(typeof(ITenantManagementDbContext))] + [ConnectionStringName("Default")] + public class BookStoreDbContext : + AbpDbContext, + IIdentityDbContext, + ITenantManagementDbContext + { + //... + + //DbSet properties for our Aggregate Roots + public DbSet Authors { get; set; } + public DbSet Books { get; set; } + public DbSet Categories { get; set; } + + //NOTE: We don't need to add DbSet, because we will be query it via using the Book entity + // public DbSet BookCategories { get; set; } + + //... + + protected override void OnModelCreating(ModelBuilder builder) + { + //... + + /* Configure your own tables/entities inside here */ + builder.Entity(b => + { + b.ToTable(BookStoreConsts.DbTablePrefix + "Authors" + BookStoreConsts.DbSchema); + b.ConfigureByConvention(); + + b.Property(x => x.Name) + .HasMaxLength(AuthorConsts.MaxNameLength) + .IsRequired(); + + b.Property(x => x.ShortBio) + .HasMaxLength(AuthorConsts.MaxShortBioLength) + .IsRequired(); + }); + + builder.Entity(b => + { + b.ToTable(BookStoreConsts.DbTablePrefix + "Books" + BookStoreConsts.DbSchema); + b.ConfigureByConvention(); + + b.Property(x => x.Name) + .HasMaxLength(BookConsts.MaxNameLength) + .IsRequired(); + + //one-to-many relationship with Author table + b.HasOne().WithMany().HasForeignKey(x => x.AuthorId).IsRequired(); + + //many-to-many relationship with Category table => BookCategories + b.HasMany(x => x.Categories).WithOne().HasForeignKey(x => x.BookId).IsRequired(); + }); + + builder.Entity(b => + { + b.ToTable(BookStoreConsts.DbTablePrefix + "Categories" + BookStoreConsts.DbSchema); + b.ConfigureByConvention(); + + b.Property(x => x.Name) + .HasMaxLength(CategoryConsts.MaxNameLength) + .IsRequired(); + }); + + builder.Entity(b => + { + b.ToTable(BookStoreConsts.DbTablePrefix + "BookCategories" + BookStoreConsts.DbSchema); + b.ConfigureByConvention(); + + //define composite key + b.HasKey(x => new { x.BookId, x.CategoryId }); + + //many-to-many configuration + b.HasOne().WithMany(x => x.Categories).HasForeignKey(x => x.BookId).IsRequired(); + b.HasOne().WithMany().HasForeignKey(x => x.CategoryId).IsRequired(); + + b.HasIndex(x => new { x.BookId, x.CategoryId }); + }); + } + } +} +``` + +* In this class, we've defined **DbSet** properties for our **Aggregate Roots** (**Book**, **Author** and **Category**). Notice, we didn't define **DbSet** for `BookCategory` class (our join table/entity). Because, the `Book` aggregate is responsible to manage it via sub-collection. + +* After that, we can use the **FluentAPI** to configure our tables in the `OnModelCreating` method of this class. + +```csharp +builder.Entity(b => +{ + //... + + //one-to-many relationship with Author table + b.HasOne().WithMany().HasForeignKey(x => x.AuthorId).IsRequired(); + + //many-to-many relationship with Category table => BookCategories + b.HasMany(x => x.Categories).WithOne().HasForeignKey(x => x.BookId).IsRequired(); +}); +``` + +* Here, we have provided the one-to-many relationship between the **Book** and the **Author** in the above code-block. + +```csharp +builder.Entity(b => +{ + //... + + //define composite key + b.HasKey(x => new { x.BookId, x.CategoryId }); + + //many-to-many configuration + b.HasOne().WithMany(x => x.Categories).HasForeignKey(x => x.BookId).IsRequired(); + b.HasOne().WithMany().HasForeignKey(x => x.CategoryId).IsRequired(); + + b.HasIndex(x => new { x.BookId, x.CategoryId }); +}); +``` + +* Here, firstly we've defined the composite key for our `BookCategory` entity. `BookId` and `CategoryId` are together composite keys for the `BookCategory` table. Then we've configured the many-to-many relationship between `Book` and `Category` table like in the above code-block. + +#### Implementing the `IBookRepository` Interface + +* After making the relevant configurations for database integration, we can now implement the `IBookRepository` interface. To do this, create a folder named `Books` in the `BookStore.EntityFrameworkCore` project and inside of this folder create a class named `EfCoreBookRepository` and update this class with the following code. + +```csharp +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Threading; +using System.Threading.Tasks; +using BookStore.Authors; +using BookStore.Categories; +using BookStore.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace BookStore.Books +{ + public class EfCoreBookRepository : EfCoreRepository, IBookRepository + { + public EfCoreBookRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider) + { + } + + public async Task> GetListAsync( + string sorting, + int skipCount, + int maxResultCount, + CancellationToken cancellationToken = default + ) + { + var query = await ApplyFilterAsync(); + + return await query + .OrderBy(!string.IsNullOrWhiteSpace(sorting) ? sorting : nameof(Book.Name)) + .PageBy(skipCount, maxResultCount) + .ToListAsync(GetCancellationToken(cancellationToken)); + } + + public async Task GetAsync(Guid id, CancellationToken cancellationToken = default) + { + var query = await ApplyFilterAsync(); + + return await query + .Where(x => x.Id == id) + .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); + } + + private async Task> ApplyFilterAsync() + { + var dbContext = await GetDbContextAsync(); + + return (await GetDbSetAsync()) + .Include(x => x.Categories) + .Join(dbContext.Set(), book => book.AuthorId, author => author.Id, + (book, author) => new {book, author}) + .Select(x => new BookWithDetails + { + Id = x.book.Id, + Name = x.book.Name, + Price = x.book.Price, + PublishDate = x.book.PublishDate, + CreationTime = x.book.CreationTime, + AuthorName = x.author.Name, + CategoryNames = (from bookCategories in x.book.Categories + join category in dbContext.Set() on bookCategories.CategoryId equals category.Id + select category.Name).ToArray() + }); + } + + public override Task> WithDetailsAsync() + { + return base.WithDetailsAsync(x => x.Categories); + } + } +} +``` + +* Here we've implemented our custom repository methods and returned the book with details (author name and categories). + +### Step 4 - (Database Migration) + +* We've integrated our entities with the database in the previous step, now we can create a new database migration and apply it to the database. So let's do that. + +* Open the `BookStore.EntityFrameworkCore` project in the terminal. And create a new database migration by using the following command. + +```bash +dotnet ef migrations add +``` + +* Then, run the `BookStore.DbMigrator` application to create the database. + +### Step 5 - (Create Application Services) -### Step 3 - (Database Migration) -### Step 4 - (Create Application Services) -### Step 5 - (UI) +### Step 6 - (UI) diff --git a/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/domain-shared-file-structure.png b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/domain-shared-file-structure.png new file mode 100644 index 0000000000000000000000000000000000000000..22a8de65999f9c7c29c08cc183417aa29327e2e6 GIT binary patch literal 24969 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