416.1 Many‐to‐many of ABP framework applications - chempkovsky/CS82ANGULAR GitHub Wiki

Note
  • Let's assume that one work phone is used by more than one employee, or one home phone is used by more than one family member. The data models implemented in the previous steps are not very suitable for realizing this fact. Instead of changing the created entities, we will create two additional ones: PhbkPhoneMtm and PhbkEmpToPhn.
  • If we follow the same implementation pattern, we should declare the classes as follows:
  • For the PhbkPhoneMtm-class it'll be as below
Click to show the code
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;

namespace rupbes.firstapp.Phbk
{
    public class PhbkPhoneMtm : Entity<int>, IMultiTenant, IHasConcurrencyStamp
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        [Display(Description = "Row id", Name = "Phone Id", Prompt = "Enter Phone Id", ShortName = "Phone Id")]
        [Required]
        public override int Id { get; protected set; }

        [StringLength(20, MinimumLength = 3, ErrorMessage = "Invalid")]
        [Display(Description = "Name of the Phone Type", Name = "Phone", Prompt = "Enter Phone", ShortName = "Phone")]
        [Required]
        public string Phone { get; protected set; } = null!;

        [Display(Description = "Row id", Name = "Phone Type Id", Prompt = "Enter Phone Type Id", ShortName = "Phone Type Id")]
        [Required]
        public int PhoneTypeIdRef { get; protected set; }

        [DisableAuditing]
        [Display(Description = "Concurrency Stamp", Name = "Concurrency Stamp", Prompt = "Enter Concurrency Stamp", ShortName = "Concurrency Stamp")]
        [StringLength(40, MinimumLength = 0, ErrorMessage = "Invalid")]
        [Required]
        [ConcurrencyCheck]
        public virtual string ConcurrencyStamp { get; set; } = default!;


        [Display(Description = "Tenant id", Name = "Tenant Id", Prompt = "Enter Tenant Id", ShortName = "Tenant Id")]
        public virtual Guid? TenantId { get; protected set; }

#if (!NOTMODELING)
        public PhbkPhoneType PhoneType { get; set; } = null!;
        public virtual List<PhbkEmpToPhn> EmpToPhns { get; set; } = null!;
#endif
        public PhbkPhoneMtm() : base()
        {
            ConcurrencyStamp = Guid.NewGuid().ToString("N");
        }

        public PhbkPhoneMtm(int id, string phone, int phoneTypeIdRef) : base(id)
        {
            Check.NotNullOrWhiteSpace(phone, nameof(Phone));
            Phone = phone;
            PhoneTypeIdRef = phoneTypeIdRef;
            ConcurrencyStamp = Guid.NewGuid().ToString("N");
        }

        public virtual void ChangeVals(string phone, int phoneTypeIdRef, string concurrencyStamp)
        {
            Check.NotNullOrWhiteSpace(phone, nameof(Phone));
            Phone = phone;
            PhoneTypeIdRef = phoneTypeIdRef;
            ConcurrencyStamp = concurrencyStamp;
        }

    }
}
  • For the PhbkEmpToPhn-class it'll be as below
Click to show the code
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;

namespace rupbes.firstapp.Phbk
{
    public class PhbkEmpToPhn : Entity, IMultiTenant, IHasConcurrencyStamp
    {
        [Display(Description = "Phone Id", Name = "Id of the Employee", Prompt = "Enter Employee Id", ShortName = "Employee Id")]
        [Required]
        public int EmpIdRef { get; protected set; }

        [Display(Description = "Phone Id", Name = "Phone Id", Prompt = "Enter Phone Id", ShortName = "Phone Id")]
        [Required]
        public int PhnIdRef { get; protected set; }

        [DisableAuditing]
        [Display(Description = "Concurrency Stamp", Name = "Concurrency Stamp", Prompt = "Enter Concurrency Stamp", ShortName = "Concurrency Stamp")]
        [StringLength(40, MinimumLength = 0, ErrorMessage = "Invalid")]
        [Required]
        [ConcurrencyCheck]
        public virtual string ConcurrencyStamp { get; set; } = default!;


        [Display(Description = "Tenant id", Name = "Tenant Id", Prompt = "Enter Tenant Id", ShortName = "Tenant Id")]
        public virtual Guid? TenantId { get; protected set; }

        public PhbkEmpToPhn() : base()
        {
            ConcurrencyStamp = Guid.NewGuid().ToString("N");
        }

        public PhbkEmpToPhn(int empIdRef, int phnIdRef ) : base()
        {
            EmpIdRef = empIdRef;
            PhnIdRef = phnIdRef;
            ConcurrencyStamp = Guid.NewGuid().ToString("N");
        }

        public override object[] GetKeys()
        {
            return new object[] { EmpIdRef, PhnIdRef };
        }

#if (!NOTMODELING)
        public PhbkPhoneMtm PhoneMtm { get; set; } = null!;
        public PhbkEmployee Employee { get; set; } = null!;
#endif

    }
}
  • For the PhbkEmployee-class we change #if (!NOTMODELING) ... #endif code block:
Click to show the code
#if (!NOTMODELING)
        public PhbkDivision Division { get; set; } = null!;
        public virtual List<PhbkPhone> Phones { get; set; } = null!;
        public virtual List<PhbkEmpToPhn> EmpToPhns { get; set; } = null!;
#endif
  • firstappDbContextCreatingExtensions-class become as follows:
Click to show the code
using Microsoft.EntityFrameworkCore;
using rupbes.firstapp.Phbk;
using Volo.Abp;
using Volo.Abp.EntityFrameworkCore.Modeling;

namespace rupbes.firstapp.EntityFrameworkCore
{
    public static class firstappDbContextCreatingExtensions
    {
        public static void ConfigurefirstappDbContext(this ModelBuilder builder)
        {
            Check.NotNull(builder, nameof(builder));

            builder.Entity<PhbkPhoneType>(b =>
            {
                b.ToTable(firstappConsts.DbTablePrefix + "PhbkPhoneTypes", firstappConsts.DbSchema);
                b.ConfigureByConvention(); //auto configure for the base class props
                                           //...
                b.HasKey(p => p.Id);
            });
            builder.Entity<PhbkEnterprise>(b =>
            {
                b.ToTable(firstappConsts.DbTablePrefix + "PhbkEnterprises", firstappConsts.DbSchema);
                b.ConfigureByConvention(); //auto configure for the base class props
                                           //...
                b.HasKey(p => p.Id);
            });
            builder.Entity<PhbkDivision>(b =>
            {
                b.ToTable(firstappConsts.DbTablePrefix + "PhbkDivisions", firstappConsts.DbSchema);
                b.ConfigureByConvention(); //auto configure for the base class props
                                           //...
                b.HasKey(p => p.Id);

                // builder.Entity<PhbkDivision>().HasOne(d => d.Enterprise)
                b.HasOne<PhbkEnterprise>()
                        //.WithMany(m => m.Divisions)
                        .WithMany()
                        .HasForeignKey(d => d.EntrprsIdRef)
                        .HasPrincipalKey(p => p.Id)
                        .IsRequired(true)
                        .OnDelete(DeleteBehavior.NoAction);
            });
            builder.Entity<PhbkEmployee>(b =>
            {
                b.ToTable(firstappConsts.DbTablePrefix + "Employes", firstappConsts.DbSchema);
                b.ConfigureByConvention(); //auto configure for the base class props
                                           //...
                b.HasKey(p => p.Id);

                //builder.Entity<PhbkEmployee>().HasOne(d => d.Division)
                b.HasOne<PhbkDivision>()
                        // .WithMany(m => m.Employees)
                        .WithMany()
                        .HasForeignKey(d => d.DivisionIdRef)
                        .HasPrincipalKey(p => p.Id)
                        .IsRequired(true)
                        .OnDelete(DeleteBehavior.NoAction);
            });

            builder.Entity<PhbkPhone>(b =>
            {
                b.ToTable(firstappConsts.DbTablePrefix + "Phones", firstappConsts.DbSchema);
                b.ConfigureByConvention(); //auto configure for the base class props
                                           //...
                b.HasKey(p => p.Id);

                // builder.Entity<PhbkPhone>().HasOne(d => d.PhoneType)
                b.HasOne<PhbkPhoneType>()
                        //.WithMany(m => m.Phones)
                        .WithMany()
                        .HasForeignKey(d => d.PhoneTypeIdRef)
                        .HasPrincipalKey(p => p.Id)
                        .IsRequired(true)
                        .OnDelete(DeleteBehavior.NoAction);

                //builder.Entity<PhbkPhone>().HasOne(d => d.Employee)
                b.HasOne<PhbkEmployee>()
                        // .WithMany(m => m.Phones)
                        .WithMany()
                        .HasForeignKey(d => d.EmployeeIdRef)
                        .HasPrincipalKey(p => p.Id)
                        .IsRequired(true)
                        .OnDelete(DeleteBehavior.NoAction);
            });
            builder.Entity<PhbkFile>(b =>
            {
                b.ToTable(firstappConsts.DbTablePrefix + "Files", firstappConsts.DbSchema);
                b.ConfigureByConvention(); //auto configure for the base class props
                                           //...
                b.HasKey(p => new { p.FileId, p.FlNm });
            });
            builder.Entity<PhbkPhoneMtm>(b =>
            {
                b.ToTable(firstappConsts.DbTablePrefix + "Phonemtms", firstappConsts.DbSchema);
                b.ConfigureByConvention(); //auto configure for the base class props
                                           //...
                b.HasKey(p => p.Id);

                // builder.Entity<PhbkPhoneMtm>().HasOne(d => d.PhoneType)
                b.HasOne<PhbkPhoneType>()
                        //.WithMany(m => m.PhoneMtms)
                        .WithMany()
                        .HasForeignKey(d => d.PhoneTypeIdRef)
                        .HasPrincipalKey(p => p.Id)
                        .IsRequired(true)
                        .OnDelete(DeleteBehavior.NoAction);
            });
            builder.Entity<PhbkEmpToPhn>(b =>
            {
                b.ToTable(firstappConsts.DbTablePrefix + "EmpToPhns", firstappConsts.DbSchema);
                b.ConfigureByConvention(); //auto configure for the base class props
                                           //...
                b.HasKey(p => new { p.EmpIdRef, p.PhnIdRef });

                // builder.Entity<EmpToPhn>().HasOne(d => d.Employee)
                b.HasOne<PhbkEmployee>()
                        //.WithMany(m => m.EmpToPhns)
                        .WithMany()
                        .HasForeignKey(d => d.EmpIdRef)
                        .HasPrincipalKey(p => p.Id)
                        .IsRequired(true)
                        .OnDelete(DeleteBehavior.NoAction);

                // builder.Entity<EmpToPhn>().HasOne(d => d.PhoneMtm)
                b.HasOne<PhbkPhoneMtm>()
                        //.WithMany(m => m.EmpToPhns)
                        .WithMany()
                        .HasForeignKey(d => d.PhnIdRef)
                        .HasPrincipalKey(p => p.Id)
                        .IsRequired(true)
                        .OnDelete(DeleteBehavior.NoAction);
            });
        }
    }
}
  • For DTO classes we turn ON with Use only root props For select method-check box
Click to show the picture

project structure

At the end
  • After completion all the steps we obtain the following user interface:
  • We only see the primary key instead of the phone number.
Click to show the picture

project structure

  • Of course, we can view the data item per item:
Click to show the picture

project structure

  • At the end we obtain bad user experience. Nobody will agree to use such software. Our approach to modeling needs to change!!!
⚠️ **GitHub.com Fallback** ⚠️