经过几天的努力Biwen.AutoClassGen终于实现了DTO复杂属性的生成
前言
距写上一篇 https://www.cnblogs.com/vipwan/p/18535459 生成DTO已经有一段时间了,
最初没有考虑复杂二级属性嵌套的实现,而是直接使用排除使用自定义的方式解决这个问题,
但是这个有些违背简约到底的初衷,并且也有好几个网友提出希望支持复杂嵌套属性DTO的功能,最近自己凑了几天时间打磨了一下,算是大致实现了这个功能
使用方式
支持特性继承
比如我们的DTO对象也需要[Require],[Range(1,10)]等校验型特征,我们只需要目标类的属性标注即可,生成的DTO也将传递这些重要的特性
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;
}
- 普通 DTO(单层映射)
 
/// <summary>
/// 没有复杂属性嵌套的 DTO
/// </summary>
[AutoDto<Person>(nameof(Person.Igrone))]//忽略掉Igrone属性
public partial record PersonDto;
- 复杂 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
本文版权归作者所有,转载请注明出处!
经过几天的努力Biwen.AutoClassGen终于实现了DTO复杂属性的生成的更多相关文章
- 终于等到你!阿里正式向 Apache Flink 贡献 Blink 源码
		
摘要: 如同我们去年12月在 Flink Forward China 峰会所约,阿里巴巴内部 Flink 版本 Blink 将于 2019 年 1 月底正式开源.今天,我们终于等到了这一刻. 阿里妹导 ...
 - MyBatis-Plus联表查询的短板,终于有一款工具补齐了
		
原创:微信公众号 码农参上,欢迎分享,转载请保留出处. 哈喽大家好啊,我是Hydra. mybatis-plus作为mybatis的增强工具,它的出现极大的简化了开发中的数据库操作,但是长久以来,它的 ...
 - 账号密码管理系统Access版本
		
哈哈,花了我整整五天时间,账号密码管理系统软件终于成功编写完成了.由于我的各大论坛的账号密码特别多,记性又不好.所以一直以来都想要这么一个软件的,但是以前学习的都是面向过程的编程语言,一直无法实现这个 ...
 - 匈牙利算法——S.B.S.
		
匈牙利算法是由匈牙利数学家Edmonds于1965年提出,因而得名.匈牙利算法是基于Hall定理中充分性证明的思想,它是部图匹配最常见的算法,该算法的核心就是寻找增广路径,它是一种用增广路径求二分图最 ...
 - 用Netty开发中间件:高并发性能优化
		
用Netty开发中间件:高并发性能优化 最近在写一个后台中间件的原型,主要是做消息的分发和透传.因为要用Java实现,所以网络通信框架的第一选择当然就是Netty了,使用的是Netty 4版本.Net ...
 - 使用scp在windows和Linux之间互传文件
		
转自:http://yangzhongfei.blog.163.com/blog/static/4610987520103141050918/ 为了进行系统维护操作,有时需要再windows和linu ...
 - np2016课程总结
		
林牧 SA16222166 课程目标 课程安排 A1a A2 A3 项目集成 环境搭建 其他方面的收获 本课心得 课程目标 通过实现一个医学辅助诊断的专家系统原型,具体为实现对血常规检测报告OCR识别 ...
 - [官方作品] 关于ES4的设首页问题
		
[官方作品] 关于ES4的设首页问题 Skyfree 发表于 2013-2-10 21:55:03 https://www.itsk.com/thread-254503-1-1.html 关于ES4设 ...
 - Windows 下 ffmpeg 转 mp4
		
最近在研究所有视频格式转 mp4 因为html5 只支持mov MP4 等格式..查阅了 很多资料发现 转成flv 很简单.. 可是要转 mp4 就难了... 经过我不屑的努力..终于转换成功了 ...
 - 大型网站系统与Java中间件实践
		
大型网站系统与Java中间件实践(贯通分布式高并发高数据高访问量网站架构与实现之权威著作,九大一线互联网公司CTO联合推荐) 曾宪杰 著 ISBN 978-7-121-22761-5 2014年4 ...
 
随机推荐
- 第四章 dubbo源码解析目录
			
9.1 客户端发起请求源码 来看一下客户端请求代码: 1 DemoService demoService = (DemoService) context.getBean("demoServi ...
 - Elasticsearch应用介绍
			
Elasticsearch 是一个分布式可扩展的实时搜索和分析引擎,一个建立在全文搜索引擎 Apache Lucene(TM) 基础上的搜索引擎.当然 Elasticsearch 并不仅仅是 Luce ...
 - redis-总结列表
			
基础 启动命令 redis-server kconfig/redis.conf 通过指定的配置文件启动服务(kconfig/redis.conf是复制过来的) redis-cli -p 6379 使用 ...
 - SpringBoot启动报错:HikariPool-1 - Exception during pool initialization.
			
不说废话,直接上错误信息: 2020-06-23 10:48:19.899 INFO 10608 --- [ main] cn.wps.ep.account.sync.Application : St ...
 - law Intermediate walkthrough pg
			
靶场很简单分数只有10分跟平常做的20分的中级靶场比确实简单 我拿来放松的 算下来30分钟解决战斗 nmap 扫到80端口web界面 是个框架 搜exp https://www.exploit-db. ...
 - 寻找AI新势力!“天翼云息壤杯”高校AI大赛火热报名中!
			
各位高校开发者们, AI舞台已就位,你准备好了吗? 为加快落实"人工智能+"行动,中国电信集团有限公司充分发挥央企在人工智能领域主力军作用,联合华为技术有限公司共同举办" ...
 - golang轻量级版本管理工具g安装使用
			
使用 g 可以在 windows 上切换使用不同版本的 golang GitHub仓库地址 https://github.com/voidint/g GitHub下载连接 https://github ...
 - Qt项目下pro文件设置库路径
			
问题 正常情况下我们会将工程的路径用一个变量来储存 例如: PROJECT_DIR=/home/$$(USER)/svn/embd_linux/app 然后后面的变量引用这个变量,例如: INCLUD ...
 - Flume - [02] Spooling Directory Source
			
一.概述 可以通过将文件放入磁盘上的 "Spooldir" 目录中来获取数据.此源会监视指定目录中的新文件,并在新文件出现时解析新文件中的事件.事件解析逻辑是可插入的.在将指定 ...
 - Scala  Map集合
			
package com.wyh.day01 object ScalaMap { def main(args: Array[String]): Unit = { /** * 不可变Map * */ // ...