当前位置:网站首页>Processing of one to many, many to many relations and functional interface in ABP framework (1)

Processing of one to many, many to many relations and functional interface in ABP framework (1)

2020-11-10 11:24:32 Wu Huacong

As we develop our business , Generally, database tables have related relationships , Except for the separate tables , Generally, it also includes one to many 、 Many to many, etc , In the actual development process , Need to combine with the system framework to do the corresponding processing , This essay is based on ABP Frame pair EF Entity 、DTO The handling of the relationship , And provide the corresponding interface to save and update the relevant data .

1、 Data processing for one to many relationships

One to many , It can also be called the relationship between the master and the subordinate table , The slave table has a foreign key associated with the master table , As shown below .

The diagram above is a simple master-slave relationship , There are only one or two simple fields in the customer information table to demonstrate , The slave table is used to record the address information of the corresponding customer .

In the table CreateUserId、CreateTime、LastModifierUserId、LastModificationTime、DeleterUserId、IsDeleted、DeletionTime、TenantId Field , It's our general design ABP Table reserved fields .

Let's start with a diagram to understand the relationship between the classes in the domain driver module under the framework .

ABP frame , The data objects that deal with the application service layer or interface layer are DTO object , Dealing with databases is entity objects , To connect by means of AutoMapper Implement mapping processing , Mapping is configured through a mapping file , Generally, we can generate according to the database table information DTO、Entity, And mapping files .

Take the customer and the customer address table as an example , Generated DTO The objects are as follows .

    /// <summary>
    ///  Create customer information ,DTO object 
    /// </summary>
    public class CreateCustomerDto : FullAuditedEntityDto<string>
    { 
        /// <summary>
        ///  Default constructor ( You need to initialize the property here )
        /// </summary>
        public CreateCustomerDto()
        {
            this.Id = Guid.NewGuid().ToString();
         }

        #region Property Members
        
        /// <summary>
        ///  full name 
        /// </summary>
        [Required]
        public virtual string Name { get; set; }

        /// <summary>
        ///  Age 
        /// </summary>
        //[Required]
        public virtual int? Age { get; set; }


        #endregion

    }

    /// <summary>
    ///  Customer information ,DTO object 
    /// </summary>
    public class CustomerDto : CreateCustomerDto
    {
    }
    /// <summary>
    ///  Create a customer address book ,DTO object 
    /// </summary>
    public class CreateCustomerAddressDto : CreationAuditedEntityDto<string>
    { 
        /// <summary>
        ///  Default constructor ( You need to initialize the property here )
        /// </summary>
        public CreateCustomerAddressDto()
        {
            this.Id = System.Guid.NewGuid().ToString(); 
         }

        #region Property Members
        
        /// <summary>
        ///  Customer ID
        /// </summary>
        //[Required]
        public virtual string Customer_ID { get; set; }

        /// <summary>
        ///  Province 
        /// </summary>
        //[Required]
        public virtual string Province { get; set; }

        /// <summary>
        ///  City 
        /// </summary>
        //[Required]
        public virtual string City { get; set; }

        /// <summary>
        ///  District and county 
        /// </summary>
        //[Required]
        public virtual string District { get; set; }

        /// <summary>
        ///  Detailed address 
        /// </summary>
        //[Required]
        public virtual string DetailAddress { get; set; }

        /// <summary>
        ///  Sort 
        /// </summary>
        //[Required]
        public virtual string SortCode { get; set; }


        #endregion

    }

    /// <summary>
    ///  Customer address book ,DTO object 
    /// </summary>
    public class CustomerAddressDto : CreateCustomerAddressDto
    {
    }

Its table corresponds to the entity class , And also DTO similar , It's just a data object dealing with a database

    /// <summary>
    ///  Customer information , Domain object 
    /// </summary>
    [Table("T_Customer")]
    public class Customer : FullAuditedEntity<string>
    { 
        /// <summary>
        ///  Default constructor ( You need to initialize the property here )
        /// </summary>
        public Customer()
        {
        }

        #region Property Members
        
        /// <summary>
        ///  full name 
        /// </summary>
        //[Required]
        public virtual string Name { get; set; }

        /// <summary>
        ///  Age 
        /// </summary>
        //[Required]
        public virtual int? Age { get; set; }

        #endregion

    }
   /// <summary>
    ///  Customer address book , Domain object 
    /// </summary>
    [Table("T_CustomerAddress")]
    public class CustomerAddress : CreationAuditedEntity<string>
    { 
        /// <summary>
        ///  Default constructor ( You need to initialize the property here )
        /// </summary>
        public CustomerAddress()
        {
        }

        #region Property Members
        
        /// <summary>
        ///  Customer ID
        /// </summary>
        //[Required]
        public virtual string Customer_ID { get; set; }

        /// <summary>
        ///  Province 
        /// </summary>
        //[Required]
        public virtual string Province { get; set; }

        /// <summary>
        ///  City 
        /// </summary>
        //[Required]
        public virtual string City { get; set; }

        /// <summary>
        ///  District and county 
        /// </summary>
        //[Required]
        public virtual string District { get; set; }

        /// <summary>
        ///  Detailed address 
        /// </summary>
        //[Required]
        public virtual string DetailAddress { get; set; }

        /// <summary>
        ///  Sort 
        /// </summary>
        //[Required]
        public virtual string SortCode { get; set; }

        /// <summary>
        ///  Customer ID
        /// </summary>
        //[Required]
        [ForeignKey("Customer_ID")]
        public virtual Customer Customer { get; set; }

        #endregion

    }

The mapping file looks like this .

    /// <summary>
    ///  Customer information , The mapping file 
    /// </summary>
    public class CustomerMapProfile : Profile  
    {
        public CustomerMapProfile()
        {
            CreateMap<CustomerDto, Customer>();
            CreateMap<Customer, CustomerDto>();
            CreateMap<CreateCustomerDto, Customer>();
        }
    }
    /// <summary>
    ///  Customer address book , The mapping file 
    /// </summary>
    public class CustomerAddressMapProfile : Profile  
    {
        public CustomerAddressMapProfile()
        {
            CreateMap<CustomerAddressDto, CustomerAddress>();
            CreateMap<CustomerAddress, CustomerAddressDto>();
            CreateMap<CreateCustomerAddressDto, CustomerAddress>();
        }
    }

And then in EFCore Add the corresponding DBSet Object can .

With these , be based on ABP On the basis of the framework, we can create data 、 Update submitted .

 

2、 Interface processing and server side of one to many relationship ABP Interface processing

But the relationship between the master and slave tables , We haven't explained in detail here , Generally, when we process data in the interface , The master table data may be displayed with the slave table data , Save it when you edit it , As shown in the following interface .

  Editing / New interface binding GridView Related display and processing of events .

We can load the empty address list in the new window , Or edit window to load the existing address list record  

  Save the new records as shown below .

        /// <summary>
        ///  Save the data in the new state 
        /// </summary>
        /// <returns></returns>
        public async override Task<bool> SaveAddNew()
        {
            CustomerDto info = tempInfo;// Must use existing local variables , Because some of the information may be used by attachments 
            SetInfo(info);

            try
            {
                #region  The new data 

                tempInfo = await CustomerApiCaller.Instance.CreateAsync(info);
                if (tempInfo != null)
                {
                    // You can add other association operations 
                    var list = GetDetailList();
                    foreach(var detailInfo in list)
                    {
                        await CustomerAddressApiCaller.Instance.InsertOrUpdateAsync(detailInfo);
                    }
                    return true;
                }
                #endregion
            }
            catch (Exception ex)
            {
                LogTextHelper.Error(ex);
                MessageDxUtil.ShowError(ex.Message);
            }
            return false;
        }    

among GetDetailList It is to get the data record in editing state

        /// <summary>
        ///  Get a list of details 
        /// </summary>
        /// <returns></returns>
        private List<CustomerAddressDto> GetDetailList()
        {
            var list = new List<CustomerAddressDto>();
            for (int i = 0; i < this.gridView1.RowCount; i++)
            {
                var detailInfo = gridView1.GetRow(i) as CustomerAddressDto;
                if (detailInfo != null)
                {
                    list.Add(detailInfo);
                }
            }
            return list;
        }

If the data is updated , The operation is similar

        /// <summary>
        ///  Data saving in editing state 
        /// </summary>
        /// <returns></returns>
        public override async Task<bool> SaveUpdated()
        {
            CustomerDto info = await CustomerApiCaller.Instance.GetAsync(ID);          
            if (info != null)
            {
                SetInfo(info);

                try
                {
                    #region  Update data 
                    tempInfo = await CustomerApiCaller.Instance.UpdateAsync(info);
                    if (tempInfo != null)
                    {
                        // You can add other association operations 
                        var list = GetDetailList();
                        foreach(var detailInfo in list)
                        {
                            await CustomerAddressApiCaller.Instance.InsertOrUpdateAsync(detailInfo);
                        }                       
                        return true;
                    }
                    #endregion
                }
                catch (Exception ex)
                {
                    LogTextHelper.Error(ex);
                    MessageDxUtil.ShowError(ex.Message);
                }
            }
           return false;
        }

We notice here that whether the address record is updated or inserted , They all use a function InsertOrUpdateAsync, This is our background judgment record is added or updated , The processing function in writing to the database .

This function is more general , We can consider writing it into a common base class interface .

Again , Client's ApiCaller call , We also need to add a simple base class interface .

With this support ,Winform The client can directly call these simple interfaces to submit the data of master-slave table .

    // You can add other association operations 
    var list = GetDetailList();
    foreach(var detailInfo in list)
    {
        await CustomerAddressApiCaller.Instance.InsertOrUpdateAsync(detailInfo);
    }  

in addition , In addition to this fine-grained interface processing , We can also put the whole DTO Package the object , In the main table DTO Object contains a list of details from the table , Then rewrite Create、Update Server application service layer interface of , Receive slave list details , Then an interface can be used to save or update the data of the master-slave table .

How to choose the way of data processing , It needs to be measured according to the business scenario .

 

3、 Combined with code generation tools, it can quickly generate background code and master-slave table interface code

Once the business rules are established , We can use code generation tools to improve development efficiency . Because the master-slave relationship is relatively unified , So we can generate this content in terms of their relationship and the way the interface handles it .

First , We open the code generation tool , Expand the table information of the corresponding database , As shown in the following interface .

choice ABP Framework code generation , Can generate background frame code , These include DTO Entity 、 Entity object 、 The mapping file , Server application layer interface and implementation .

Generate Winform In the master-slave interface , choice Winform Code generation , As shown in the following interface .

Then in the pop-up interface, select the generation tab of the master-slave table interface .

 

版权声明
本文为[Wu Huacong]所创,转载请带上原文链接,感谢