414 Fourth entity of ABP framework applications - chempkovsky/CS82ANGULAR GitHub Wiki

Note
Entity
  • In the Phbk folder of the rupbes.firstapp.Domain.csproj project, create a class as shown below. We inherit the class from Entity
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 PhbkEmployee : Entity<int>, IMultiTenant, IHasConcurrencyStamp
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        [Display(Description = "Row id", Name = "Id of the Employee", Prompt = "Enter Employee  Id", ShortName = "Employee Id")]
        [Required]
        public override int Id { get; protected set; }

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

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

        [Display(Description = "Row id", Name = "Employee Second Name", Prompt = "Enter Employee Second Name", ShortName = "Second Name")]
        [StringLength(25, ErrorMessage = "Invalid")]
        public string? EmpSecondName { get; protected set; }

        [Display(Description = "Row id", Name = "Id of the Division", Prompt = "Enter Division Id", ShortName = "Division Id")]
        [Required]
        public int DivisionIdRef { 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 PhbkEmployee() : base()
        {
            ConcurrencyStamp = Guid.NewGuid().ToString("N");
        }

        public PhbkEmployee(int id, string empFirstName, string? empLastName, string? empSecondName, int divisionIdRef ) : base(id)
        {
            Check.NotNullOrWhiteSpace(empFirstName, nameof(EmpFirstName));
            Check.NotNullOrWhiteSpace(empLastName, nameof(EmpLastName));
            EmpFirstName = empFirstName;
            EmpLastName = empLastName;
            EmpSecondName = empSecondName;
            DivisionIdRef = divisionIdRef;
            ConcurrencyStamp = Guid.NewGuid().ToString("N");
        }

        public virtual void ChangeVals(string empFirstName, string? empLastName, string? empSecondName, int divisionIdRef, string concurrencyStamp)
        {
            Check.NotNullOrWhiteSpace(empFirstName, nameof(EmpFirstName));
            Check.NotNullOrWhiteSpace(empLastName, nameof(EmpLastName));
            EmpFirstName = empFirstName;
            EmpLastName = empLastName;
            EmpSecondName = empSecondName;
            DivisionIdRef = divisionIdRef;
            ConcurrencyStamp = concurrencyStamp;
        }


#if (!NOTMODELING)
        public PhbkDivision Division { get; set; } = null!;
#endif

    }
}
  • We have to modify PhbkDivision-class as well.
Click to show the code
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Volo.Abp;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;

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

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

        [Display(Description = "Description of the Enterprise Division", Name = "Description of the Division", Prompt = "Enter Enterprise Division Description", ShortName = "Division Description")]
        [StringLength(250, ErrorMessage = "Invalid")]
        public string? DivisionDesc { get; 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; }


        [Display(Description = "Row id", Name = "Id of the Enterprise", Prompt = "Enter Enterprise  Id", ShortName = "Enterprise Id")]
        [Required]
        public int EntrprsIdRef { get; set; }

#if (!NOTMODELING)
        public PhbkEnterprise Enterprise { get; set; } = null!;
        public List<PhbkEmployee> Employees { get; set; } = null!;
#endif

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

        public PhbkDivision(int id, string divisionName, string? divisionDesc, int entrprsIdRef) : base(id)
        {
            Check.NotNullOrWhiteSpace(divisionName, nameof(DivisionName));
            DivisionName = divisionName;
            DivisionDesc = divisionDesc;
            EntrprsIdRef = entrprsIdRef;
            ConcurrencyStamp = Guid.NewGuid().ToString("N");
        }

        public virtual void ChangeVals(string divisionName, string? divisionDesc, int entrprsIdRef, string concurrencyStamp)
        {
            Check.NotNullOrWhiteSpace(divisionName, nameof(DivisionName));
            DivisionName = divisionName;
            DivisionDesc = divisionDesc;
            EntrprsIdRef = entrprsIdRef;
            ConcurrencyStamp = concurrencyStamp;
        }

    }
}
Modify DBContext
Click to show the code
    protected override void OnModelCreating(ModelBuilder builder)
    {

#if (!NOTMODELING)
        builder.Entity<PhbkFile>().HasKey(p => new { p.FileId, p.FlNm });
        builder.Entity<PhbkPhoneType>().HasKey(p => p.Id);
        builder.Entity<PhbkEnterprise>().HasKey(p => p.Id);
        builder.Entity<PhbkDivision>().HasKey(p => p.Id);
        builder.Entity<PhbkEmployee>().HasKey(p => p.Id);
#endif
        base.OnModelCreating(builder);
        /* Include modules to your migration db context */
        builder.ConfigurePermissionManagement();
        builder.ConfigureSettingManagement();
        builder.ConfigureBackgroundJobs();
        builder.ConfigureAuditLogging();
        builder.ConfigureFeatureManagement();
        builder.ConfigureIdentity();
        builder.ConfigureOpenIddict();
        builder.ConfigureTenantManagement();
        builder.ConfigureBlobStoring();
        /* Configure your own tables/entities inside here */
        //builder.Entity<YourEntity>(b =>
        //{
        //    b.ToTable(firstappConsts.DbTablePrefix + "YourEntities", firstappConsts.DbSchema);
        //    b.ConfigureByConvention(); //auto configure for the base class props
        //    //...
        //});
        builder.ConfigurefirstappDbContext();
#if (!NOTMODELING)
        builder.Entity<PhbkDivision>().HasOne(d => d.Enterprise)
                .WithMany(m => m.Divisions)
                .HasForeignKey(d => d.EntrprsIdRef)
                .HasPrincipalKey(p => p.Id)
                .IsRequired(true)
                .OnDelete(DeleteBehavior.NoAction);
        builder.Entity<PhbkEmployee>().HasOne(d => d.Division)
                .WithMany(m => m.Employees)
                .HasForeignKey(d => d.DivisionIdRef)
                .HasPrincipalKey(p => p.Id)
                .IsRequired(true)
                .OnDelete(DeleteBehavior.NoAction);
#endif
    }
  • ConfigurefirstappDbContext must be as follows:
    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<PhbkFile>(b =>
            {
                b.ToTable(firstappConsts.DbTablePrefix + "PhbkFiles", firstappConsts.DbSchema);
                b.ConfigureByConvention(); //auto configure for the base class props
                                           //...
                b.HasKey(p => new { p.FileId, p.FlNm });
            });
            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);
            });

        }
    }

Please take a look at b.HasOne<PhbkDivision>()-operator. We declare foreign key without using .WithMany(m => m.Employees) and .HasOne(d => d.Division) operators since Division-property and Employees-property declared to be used only at modeling time

#if (!NOTMODELING)
        public PhbkDivision Division { get; set; } = null!;
#endif

...

#if (!NOTMODELING)
        public List<PhbkEmployee> Employees { get; set; } = null!;

#endif
  • Foreign key will be as follows:
Click to show the picture

project structure

Dto
Click to show the picture

project structure

  • This is how the prefix is ​​defined:
Click to show the picture

project structure

Web Api
  • Repeat the steps of the 405 First Web Api Service (PhoneType)
  • Please note that Use only root props for select method-checkbox is ON. It means DEEntrprsName-prop and DivisionName-prop will not be generated for DTO-class. This is because we are using the Apb-repo class.
Click to show the picture

project structure

Typescript Classes
Click to show the picture

project structure

  • UI Form properties is as follows:
Click to show the picture

project structure

  • DEEntrprsName-prop and DivisionName-prop have SearchDialog-input type even for Delete and View-forms. If we assign readonly or default-type for DEEntrprsName-prop and DivisionName-prop than they will not be displayed on Delete and View-forms.

  • Modify app-routing.module.ts-file for PhbkEmployee-entity as before.

  • For PhbkDivision-entity:

    • Regenerate routing-code again, as the routing items number has changed
    • Regenerate typescript code again for:
      • services (014400-AbpWebApiService.json-batch)
      • rv-form
      • rlist-form
      • rdlist-form
  • For PhbkEnterprise-entity:

    • Regenerate routing-code again, as the routing items number has changed

Modify route.provider.ts-file as follows:

Click to show the code
import { RoutesService, eLayoutType } from '@abp/ng.core';
import { inject, provideAppInitializer } from '@angular/core';

export const APP_ROUTE_PROVIDER = [
  provideAppInitializer(() => {
    configureRoutes();
  }),
];

function configureRoutes() {
  const routes = inject(RoutesService);
  routes.add([
      {
        path: '/',
        name: '::Menu:Home',
        iconClass: 'fas fa-home',
        order: 1,
        layout: eLayoutType.application,
      },
      {
        path: '/RDLPhbkPhoneTypeDto',
        name: 'Phone Type Dlg',
        requiredPolicy: 'firstapp.PhbkPhoneTypeDto',
        iconClass: 'fas fa-phone',
        order: 20,
        layout: eLayoutType.application,
      },
      {
        path: '/PhbkPhoneTypeDto',
        name: 'Phone Type',
        requiredPolicy: 'firstapp.PhbkPhoneTypeDto',
        iconClass: 'fas fa-phone',
        order: 30,
        layout: eLayoutType.application,
      },

      {
        path: '/RDLPhbkFileDto',
        name: 'Phone book file Dlg',
        requiredPolicy: 'firstapp.PhbkFileDto',
        iconClass: 'fas fa-file',
        order: 40,
        layout: eLayoutType.application,
      },
      {
        path: '/PhbkFileDto',
        name: 'Phone book file',
        requiredPolicy: 'firstapp.PhbkFileDto',
        iconClass: 'fas fa-file',
        order: 50,
        layout: eLayoutType.application,
      }


      {
        name: 'firstapp::Psn:PhbkEnterpriseDto',
        group: 'firstapp::Psn:PhbkEnterpriseDto',
        order: 11,
        iconClass: 'fas fa-building',
      },      
      {
        path: '/RDLPhbkEnterpriseDto',
        name: 'Enterprise Dlg',
        requiredPolicy: 'firstapp.PhbkEnterpriseDto',
        iconClass: 'fas fa-building',
        parentName: 'firstapp::Psn:PhbkEnterpriseDto',
        order: 40,
        layout: eLayoutType.application,
      },
      {
        path: '/PhbkEnterpriseDto',
        name: 'Enterprise',
        requiredPolicy: 'firstapp.PhbkEnterpriseDto',
        parentName: 'firstapp::Psn:PhbkEnterpriseDto',
        iconClass: 'fas fa-building',
        order: 50,
        layout: eLayoutType.application,
      },

      {
        name: 'firstapp::Psn:PhbkDivisionDto',
        group: 'firstapp::Psn:PhbkDivisionDto',
        order: 12,
        iconClass: 'fas fa-building',
      },      
      {
        path: '/RDLPhbkDivisionDto',
        name: 'Division Dlg',
        requiredPolicy: 'firstapp.PhbkDivisionDto',
        parentName: 'firstapp::Psn:PhbkDivisionDto',
        iconClass: 'fas fa-building',
        order: 60,
        layout: eLayoutType.application,
      },
      {
        path: '/PhbkDivisionDto',
        name: 'Division',
        requiredPolicy: 'firstapp.PhbkDivisionDto',
        parentName: 'firstapp::Psn:PhbkDivisionDto',
        iconClass: 'fas fa-building',
        order: 70,
        layout: eLayoutType.application,
      },

      {
        name: 'firstapp::Psn:PhbkEmployeeDto',
        group: 'firstapp::Psn:PhbkEmployeeDto',
        order: 13,
        iconClass: 'fas fa-user-tie',
      },      
      {
        path: '/RDLPhbkEmployeeDto',
        name: 'Employee Dlg',
        requiredPolicy: 'firstapp.PhbkEmployeeDto',
        parentName: 'firstapp::Psn:PhbkEmployeeDto',
        iconClass: 'fas fa-user-tie',
        order: 80,
        layout: eLayoutType.application,
      },
      {
        path: '/PhbkEmployeeDto',
        name: 'Employee',
        requiredPolicy: 'firstapp.PhbkEmployeeDto',
        parentName: 'firstapp::Psn:PhbkEmployeeDto',
        iconClass: 'fas fa-user-tie',
        order: 90,
        layout: eLayoutType.application,
      },


  ]);
}
⚠️ **GitHub.com Fallback** ⚠️