经过几天的努力Biwen.AutoClassGen终于实现了DTO复杂属性的生成

前言

距写上一篇 https://www.cnblogs.com/vipwan/p/18535459 生成DTO已经有一段时间了,

最初没有考虑复杂二级属性嵌套的实现,而是直接使用排除使用自定义的方式解决这个问题,

但是这个有些违背简约到底的初衷,并且也有好几个网友提出希望支持复杂嵌套属性DTO的功能,最近自己凑了几天时间打磨了一下,算是大致实现了这个功能

使用方式

支持特性继承

比如我们的DTO对象也需要[Require],[Range(1,10)]等校验型特征,我们只需要目标类的属性标注即可,生成的DTO也将传递这些重要的特性,对于OpenApi文档以及一些验证场景会相当有帮助:
image

public class Person
{
    [Required]
    public string Name { get; set; } = string.Empty;
    [Required, Range(0, 200)]
    public int Age { get; set; }
}

生成的DTO:

/// <inheritdoc cref = "Person.Name"/>
[Required]
public string Name { get; set; }
/// <inheritdoc cref = "Person.Age"/>
[Required]
[Range(0, 200)]
public int Age { get; set; }
支持复杂属性嵌套生成

实体定义示例:

// 主实体
public class Person
{
    [Required]
    public string Name { get; set; } = string.Empty;
    [Required, Range(0, 200)]
    public int Age { get; set; }
    // 嵌套对象
    public Address Address { get; set; } = new();
    // 集合属性
    public List<Hobby> Hobbies { get; set; } = [];
    // 使用特性标记忽略的属性
    [AutoDtoIgroned]
    public string Igrone2 { get; set; } = null!;
}

// 嵌套实体
public class Address
{
    [Required]
    public string Street { get; set; } = string.Empty;
    [Required]
    public string City { get; set; } = string.Empty;
    [Required]
    public string State { get; set; } = string.Empty;
    [Required]
    public string ZipCode { get; set; } = string.Empty;
}

// 集合项实体
public class Hobby
{
    [Required]
    public string Name { get; set; } = string.Empty;
    [Required]
    public string Description { get; set; } = string.Empty;
    // 多层嵌套
    public HobbyExtend Extend { get; set; } = new();
}

public class HobbyExtend
{
    public string Extend1 { get; set; } = string.Empty;
    public string Extend2 { get; set; } = string.Empty;
    public InnerExtend Extend3 { get; set; } = new();
}

public class InnerExtend
{
    public string InnerExtendMsg { get; set; } = string.Empty;
}
  1. 普通 DTO(单层映射)
/// <summary>
/// 没有复杂属性嵌套的 DTO
/// </summary>
[AutoDto<Person>(nameof(Person.Igrone))]//忽略掉Igrone属性
public partial record PersonDto;
  1. 复杂 DTO(多层嵌套)
/// <summary>
/// 模拟的复杂 DTO
/// </summary>
[AutoDto<Person>(nameof(Person.Igrone))]
[AutoDtoComplex(5)]//≥2即表示多层嵌套生成
public partial record PersonComplexDto;

生成的代码样例:

生成DTO,并对象生成映射扩展:MapperToXXX,以及IQuerylable扩展ProjectToXXX:
并且生成了预留partial扩展,如果存在FirstName + LastName -> FullName这种情况,你可以自己实现partial部分!


// <auto-generated />
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

#pragma warning disable

//generate Person-PersonComplexDto
namespace Biwen.AutoClassGen.TestConsole.Dtos
{
    using Biwen.AutoClassGen.TestConsole.Dtos;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;

    public partial record class PersonComplexDto
    {
        /// <inheritdoc cref = "Person.Name"/>
        [Required]
        public string Name { get; set; }

        /// <inheritdoc cref = "Person.Age"/>
        [Required]
        [Range(0, 200)]
        public int Age { get; set; }
        /// <inheritdoc cref = "Person.Address"/>
        public AddressDto Address { get; set; }
        /// <inheritdoc cref = "Person.Hobbies"/>
        public System.Collections.Generic.List<HobbyDto> Hobbies { get; set; }
    }
}

namespace Biwen.AutoClassGen.TestConsole.Dtos
{
    using Biwen.AutoClassGen.TestConsole.Dtos;
    using System.Linq;

    public static partial class PersonToPersonComplexDtoExtentions
    {
        /// <summary>
        /// custom mapper
        /// </summary>
        static partial void MapperToPartial(Person from, PersonComplexDto to);
        /// <summary>
        /// mapper to PersonComplexDto
        /// </summary>
        /// <returns></returns>
        public static PersonComplexDto MapperToPersonComplexDto(this Person model)
        {
            if (model == null)
                return null;
            var retn = new PersonComplexDto()
            {
                Name = model.Name,
                Age = model.Age,
                Address = model.Address?.MapperToAddressDto(),
                Hobbies = model.Hobbies != null ? model.Hobbies.Select(x => x?.MapperToHobbyDto()).ToList() : null,
            };
            MapperToPartial(model, retn);
            return retn;
        }

        /// <summary>
        /// ProjectTo PersonComplexDto
        /// </summary>
        public static IQueryable<PersonComplexDto> ProjectToPersonComplexDto(this IQueryable<Person> query)
        {
            return query.Select(model => model.MapperToPersonComplexDto());
        }
    }

    public static partial class PersonComplexDtoToPersonExtentions
    {
        /// <summary>
        /// custom mapper
        /// </summary>
        static partial void MapperToPartial(PersonComplexDto from, Person to);
        /// <summary>
        /// mapper to Person
        /// </summary>
        /// <returns></returns>
        public static Person MapperToPerson(this PersonComplexDto model)
        {
            if (model == null)
                return null;
            var retn = new Person()
            {
                Name = model.Name,
                Age = model.Age,
                Address = model.Address?.MapperToAddress(),
                Hobbies = model.Hobbies != null ? model.Hobbies.Select(x => x?.MapperToHobby()).ToList() : null,
            };
            MapperToPartial(model, retn);
            return retn;
        }
    }
}

//generate Person-PersonDto
namespace Biwen.AutoClassGen.TestConsole.Dtos
{
    using Biwen.AutoClassGen.TestConsole.Dtos;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;

    public partial record class PersonDto
    {
        /// <inheritdoc cref = "Person.Name"/>
        [Required]
        public string Name { get; set; }

        /// <inheritdoc cref = "Person.Age"/>
        [Required]
        [Range(0, 200)]
        public int Age { get; set; }
        /// <inheritdoc cref = "Person.Address"/>
        public Biwen.AutoClassGen.TestConsole.Dtos.Address Address { get; set; }
        /// <inheritdoc cref = "Person.Hobbies"/>
        public System.Collections.Generic.List<Biwen.AutoClassGen.TestConsole.Dtos.Hobby> Hobbies { get; set; }
    }
}

namespace Biwen.AutoClassGen.TestConsole.Dtos
{
    using Biwen.AutoClassGen.TestConsole.Dtos;
    using System.Linq;

    public static partial class PersonToPersonDtoExtentions
    {
        /// <summary>
        /// custom mapper
        /// </summary>
        static partial void MapperToPartial(Person from, PersonDto to);
        /// <summary>
        /// mapper to PersonDto
        /// </summary>
        /// <returns></returns>
        public static PersonDto MapperToPersonDto(this Person model)
        {
            if (model == null)
                return null;
            var retn = new PersonDto()
            {
                Name = model.Name,
                Age = model.Age,
                Address = model.Address,
                Hobbies = model.Hobbies,
            };
            MapperToPartial(model, retn);
            return retn;
        }

        /// <summary>
        /// ProjectTo PersonDto
        /// </summary>
        public static IQueryable<PersonDto> ProjectToPersonDto(this IQueryable<Person> query)
        {
            return query.Select(model => model.MapperToPersonDto());
        }
    }

    public static partial class PersonDtoToPersonExtentions
    {
        /// <summary>
        /// custom mapper
        /// </summary>
        static partial void MapperToPartial(PersonDto from, Person to);
        /// <summary>
        /// mapper to Person
        /// </summary>
        /// <returns></returns>
        public static Person MapperToPerson(this PersonDto model)
        {
            if (model == null)
                return null;
            var retn = new Person()
            {
                Name = model.Name,
                Age = model.Age,
                Address = model.Address,
                Hobbies = model.Hobbies,
            };
            MapperToPartial(model, retn);
            return retn;
        }
    }
}

//generate Address-AddressDto
namespace Biwen.AutoClassGen.TestConsole.Dtos
{
    using Biwen.AutoClassGen.TestConsole.Dtos;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;

    public partial class AddressDto
    {
        /// <inheritdoc cref = "Address.Street"/>
        [Required]
        public string Street { get; set; }

        /// <inheritdoc cref = "Address.City"/>
        [Required]
        public string City { get; set; }

        /// <inheritdoc cref = "Address.State"/>
        [Required]
        public string State { get; set; }

        /// <inheritdoc cref = "Address.ZipCode"/>
        [Required]
        public string ZipCode { get; set; }
    }
}

namespace Biwen.AutoClassGen.TestConsole.Dtos
{
    using Biwen.AutoClassGen.TestConsole.Dtos;
    using System.Linq;
    using System.Net;

    public static partial class AddressToAddressDtoExtentions
    {
        /// <summary>
        /// custom mapper
        /// </summary>
        static partial void MapperToPartial(Address from, AddressDto to);
        /// <summary>
        /// mapper to AddressDto
        /// </summary>
        /// <returns></returns>
        public static AddressDto MapperToAddressDto(this Address model)
        {
            if (model == null)
                return null;
            var retn = new AddressDto()
            {
                Street = model.Street,
                City = model.City,
                State = model.State,
                ZipCode = model.ZipCode,
            };
            MapperToPartial(model, retn);
            return retn;
        }

        /// <summary>
        /// ProjectTo AddressDto
        /// </summary>
        public static IQueryable<AddressDto> ProjectToAddressDto(this IQueryable<Address> query)
        {
            return query.Select(model => model.MapperToAddressDto());
        }
    }

    public static partial class AddressDtoToAddressExtentions
    {
        /// <summary>
        /// custom mapper
        /// </summary>
        static partial void MapperToPartial(AddressDto from, Address to);
        /// <summary>
        /// mapper to Address
        /// </summary>
        /// <returns></returns>
        public static Address MapperToAddress(this AddressDto model)
        {
            if (model == null)
                return null;
            var retn = new Address()
            {
                Street = model.Street,
                City = model.City,
                State = model.State,
                ZipCode = model.ZipCode,
            };
            MapperToPartial(model, retn);
            return retn;
        }
    }
}

//generate Hobby-HobbyDto
namespace Biwen.AutoClassGen.TestConsole.Dtos
{
    using Biwen.AutoClassGen.TestConsole.Dtos;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;

    public partial class HobbyDto
    {
        /// <inheritdoc cref = "Hobby.Name"/>
        [Required]
        public string Name { get; set; }

        /// <inheritdoc cref = "Hobby.Description"/>
        [Required]
        public string Description { get; set; }
        /// <inheritdoc cref = "Hobby.Extend"/>
        public HobbyExtendDto Extend { get; set; }
    }
}

namespace Biwen.AutoClassGen.TestConsole.Dtos
{
    using Biwen.AutoClassGen.TestConsole.Dtos;
    using System.Linq;

    public static partial class HobbyToHobbyDtoExtentions
    {
        /// <summary>
        /// custom mapper
        /// </summary>
        static partial void MapperToPartial(Hobby from, HobbyDto to);
        /// <summary>
        /// mapper to HobbyDto
        /// </summary>
        /// <returns></returns>
        public static HobbyDto MapperToHobbyDto(this Hobby model)
        {
            if (model == null)
                return null;
            var retn = new HobbyDto()
            {
                Name = model.Name,
                Description = model.Description,
                Extend = model.Extend?.MapperToHobbyExtendDto(),
            };
            MapperToPartial(model, retn);
            return retn;
        }

        /// <summary>
        /// ProjectTo HobbyDto
        /// </summary>
        public static IQueryable<HobbyDto> ProjectToHobbyDto(this IQueryable<Hobby> query)
        {
            return query.Select(model => model.MapperToHobbyDto());
        }
    }

    public static partial class HobbyDtoToHobbyExtentions
    {
        /// <summary>
        /// custom mapper
        /// </summary>
        static partial void MapperToPartial(HobbyDto from, Hobby to);
        /// <summary>
        /// mapper to Hobby
        /// </summary>
        /// <returns></returns>
        public static Hobby MapperToHobby(this HobbyDto model)
        {
            if (model == null)
                return null;
            var retn = new Hobby()
            {
                Name = model.Name,
                Description = model.Description,
                Extend = model.Extend?.MapperToHobbyExtend(),
            };
            MapperToPartial(model, retn);
            return retn;
        }
    }
}

//generate HobbyExtend-HobbyExtendDto
namespace Biwen.AutoClassGen.TestConsole.Dtos
{
    using Biwen.AutoClassGen.TestConsole.Dtos;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;

    public partial class HobbyExtendDto
    {
        /// <inheritdoc cref = "HobbyExtend.Extend1"/>
        public string Extend1 { get; set; }
        /// <inheritdoc cref = "HobbyExtend.Extend2"/>
        public string Extend2 { get; set; }
        /// <inheritdoc cref = "HobbyExtend.Extend3"/>
        public InnerExtendDto Extend3 { get; set; }
    }
}

namespace Biwen.AutoClassGen.TestConsole.Dtos
{
    using Biwen.AutoClassGen.TestConsole.Dtos;
    using System.Linq;

    public static partial class HobbyExtendToHobbyExtendDtoExtentions
    {
        /// <summary>
        /// custom mapper
        /// </summary>
        static partial void MapperToPartial(HobbyExtend from, HobbyExtendDto to);
        /// <summary>
        /// mapper to HobbyExtendDto
        /// </summary>
        /// <returns></returns>
        public static HobbyExtendDto MapperToHobbyExtendDto(this HobbyExtend model)
        {
            if (model == null)
                return null;
            var retn = new HobbyExtendDto()
            {
                Extend1 = model.Extend1,
                Extend2 = model.Extend2,
                Extend3 = model.Extend3?.MapperToInnerExtendDto(),
            };
            MapperToPartial(model, retn);
            return retn;
        }

        /// <summary>
        /// ProjectTo HobbyExtendDto
        /// </summary>
        public static IQueryable<HobbyExtendDto> ProjectToHobbyExtendDto(this IQueryable<HobbyExtend> query)
        {
            return query.Select(model => model.MapperToHobbyExtendDto());
        }
    }

    public static partial class HobbyExtendDtoToHobbyExtendExtentions
    {
        /// <summary>
        /// custom mapper
        /// </summary>
        static partial void MapperToPartial(HobbyExtendDto from, HobbyExtend to);
        /// <summary>
        /// mapper to HobbyExtend
        /// </summary>
        /// <returns></returns>
        public static HobbyExtend MapperToHobbyExtend(this HobbyExtendDto model)
        {
            if (model == null)
                return null;
            var retn = new HobbyExtend()
            {
                Extend1 = model.Extend1,
                Extend2 = model.Extend2,
                Extend3 = model.Extend3?.MapperToInnerExtend(),
            };
            MapperToPartial(model, retn);
            return retn;
        }
    }
}

//generate InnerExtend-InnerExtendDto
namespace Biwen.AutoClassGen.TestConsole.Dtos
{
    using Biwen.AutoClassGen.TestConsole.Dtos;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;

    public partial class InnerExtendDto
    {
        /// <inheritdoc cref = "InnerExtend.InnerExtendMsg"/>
        public string InnerExtendMsg { get; set; }
    }
}

namespace Biwen.AutoClassGen.TestConsole.Dtos
{
    using Biwen.AutoClassGen.TestConsole.Dtos;
    using System.Linq;

    public static partial class InnerExtendToInnerExtendDtoExtentions
    {
        /// <summary>
        /// custom mapper
        /// </summary>
        static partial void MapperToPartial(InnerExtend from, InnerExtendDto to);
        /// <summary>
        /// mapper to InnerExtendDto
        /// </summary>
        /// <returns></returns>
        public static InnerExtendDto MapperToInnerExtendDto(this InnerExtend model)
        {
            if (model == null)
                return null;
            var retn = new InnerExtendDto()
            {
                InnerExtendMsg = model.InnerExtendMsg,
            };
            MapperToPartial(model, retn);
            return retn;
        }

        /// <summary>
        /// ProjectTo InnerExtendDto
        /// </summary>
        public static IQueryable<InnerExtendDto> ProjectToInnerExtendDto(this IQueryable<InnerExtend> query)
        {
            return query.Select(model => model.MapperToInnerExtendDto());
        }
    }

    public static partial class InnerExtendDtoToInnerExtendExtentions
    {
        /// <summary>
        /// custom mapper
        /// </summary>
        static partial void MapperToPartial(InnerExtendDto from, InnerExtend to);
        /// <summary>
        /// mapper to InnerExtend
        /// </summary>
        /// <returns></returns>
        public static InnerExtend MapperToInnerExtend(this InnerExtendDto model)
        {
            if (model == null)
                return null;
            var retn = new InnerExtend()
            {
                InnerExtendMsg = model.InnerExtendMsg,
            };
            MapperToPartial(model, retn);
            return retn;
        }
    }
}
#pragma warning restore

最后

以上代码完整的介绍了最近实现的功能,最后你可以使用我最新发布的nuget包体验:

<ItemGroup>
   <PackageReference Include="Biwen.AutoClassGen.Attributes" Version="1.7.0" />
   <PackageReference Include="Biwen.AutoClassGen" Version="1.7.0" PrivateAssets="all" />
</ItemGroup>

如果你对完整的实现感兴趣可以移步我的GitHub仓储,欢迎star https://github.com/vipwan/Biwen.AutoClassGen

本文版权归作者所有,转载请注明出处!

原创作者: vipwan 转载于: https://www.cnblogs.com/vipwan/p/18815596
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值