Location>code7788 >text

After several days of hard work, the generation of DTO complex attributes was finally realized.

Popularity:590 ℃/2025-04-08 22:29:50

Preface

From the previous article/vipwan/p/18535459It has been a while since DTO was generated.

initialNo considerationThe implementation of complex secondary attribute nesting is directly solved by excluding custom methods.

However, this is a bit contrary to the original intention of simplicity, and several netizens have also proposed that they hope to support the function of complex nested attributes DTO. Recently, they have been polished for a few days, which has roughly implemented this function.

How to use

Support feature inheritance

For example, our DTO object also needs it[Require],[Range(1,10)]As for the verification feature, we only need the attribute annotation of the target class, and the generated DTO will also pass these important features.

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

Generated DTO:

/// <inheritdoc cref = ""/>
[Required]
public string Name { get; set; }
/// <inheritdoc cref = ""/>
[Required]
[Range(0, 200)]
public int Age { get; set; }

Supports nested generation of complex properties

Entity definition example:

// Main entity
 public class Person
 {
     [Required]
     public string Name { get; set; } = ;
     [Required, Range(0, 200)]
     public int Age { get; set; }
     // Nested objects
     public Address Address { get; set; } = new();
     // Collection properties
     public List<Hobby> Hobbies { get; set; } = [];
     // Use attributes to ignore
     [AutoDtoIgroned]
     public string Igrone2 { get; set; } = null!;
 }

 // Nested entities
 public class Address
 {
     [Required]
     public string Street { get; set; } = ;
     [Required]
     public string City { get; set; } = ;
     [Required]
     public string State { get; set; } = ;
     [Required]
     public string ZipCode { get; set; } = ;
 }

 // Collection item entity
 public class Hobby
 {
     [Required]
     public string Name { get; set; } = ;
     [Required]
     public string Description { get; set; } = ;
     // Multi-layer nesting
     public HobbyExtend Extend { get; set; } = new();
 }

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

 public class InnerExtend
 {
     public string InnerExtendMsg { get; set; } = ;
 }
  1. Normal DTO (Single-layer Mapping)
/// <summary>
 /// DTO without complex properties nested
 /// </summary>
 [AutoDto<Person>(nameof())]//Ignore the Igrone attribute
 public partial record PersonDto;
  1. Complex DTO (multi-layer nesting)
/// <summary>
 /// Simulated complex DTO
 /// </summary>
 [AutoDto<Person>(nameof())]
 [AutoDtoComplex(5)]//≥2 means multi-layer nested generation
 public partial record PersonComplexDto;

Generated code example:

Generate DTO and object generate mapping extensions:MapperToXXX, and IQuerylable extensionProjectToXXX:
And a reservation was generatedpartialExtend, if it existsFirstName + LastName -> FullNameIn this case, you can implement the partial part yourself!


// <auto-generated />
using System;
using ;
using ;
using ;
using ;
using ;
using ;

#pragma warning disable

//generate Person-PersonComplexDto
namespace 
{
    using ;
    using ;
    using ;

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

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

namespace 
{
    using ;
    using ;

    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 = ,
                Age = ,
                Address = ?.MapperToAddressDto(),
                Hobbies =  != null ? (x => x?.MapperToHobbyDto()).ToList() : null,
            };
            MapperToPartial(model, retn);
            return retn;
        }

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

    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 = ,
                Age = ,
                Address = ?.MapperToAddress(),
                Hobbies =  != null ? (x => x?.MapperToHobby()).ToList() : null,
            };
            MapperToPartial(model, retn);
            return retn;
        }
    }
}

//generate Person-PersonDto
namespace 
{
    using ;
    using ;
    using ;

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

        /// <inheritdoc cref = ""/>
        [Required]
        [Range(0, 200)]
        public int Age { get; set; }
        /// <inheritdoc cref = ""/>
        public  Address { get; set; }
        /// <inheritdoc cref = ""/>
        public <> Hobbies { get; set; }
    }
}

namespace 
{
    using ;
    using ;

    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 = ,
                Age = ,
                Address = ,
                Hobbies = ,
            };
            MapperToPartial(model, retn);
            return retn;
        }

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

    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 = ,
                Age = ,
                Address = ,
                Hobbies = ,
            };
            MapperToPartial(model, retn);
            return retn;
        }
    }
}

//generate Address-AddressDto
namespace 
{
    using ;
    using ;
    using ;

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

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

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

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

namespace 
{
    using ;
    using ;
    using ;

    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 = ,
                City = ,
                State = ,
                ZipCode = ,
            };
            MapperToPartial(model, retn);
            return retn;
        }

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

    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 = ,
                City = ,
                State = ,
                ZipCode = ,
            };
            MapperToPartial(model, retn);
            return retn;
        }
    }
}

//generate Hobby-HobbyDto
namespace 
{
    using ;
    using ;
    using ;

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

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

namespace 
{
    using ;
    using ;

    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 = ,
                Description = ,
                Extend = ?.MapperToHobbyExtendDto(),
            };
            MapperToPartial(model, retn);
            return retn;
        }

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

    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 = ,
                Description = ,
                Extend = ?.MapperToHobbyExtend(),
            };
            MapperToPartial(model, retn);
            return retn;
        }
    }
}

//generate HobbyExtend-HobbyExtendDto
namespace 
{
    using ;
    using ;
    using ;

    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 
{
    using ;
    using ;

    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 (model => ());
        }
    }

    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 
{
    using ;
    using ;
    using ;

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

namespace 
{
    using ;
    using ;

    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 = ,
            };
            MapperToPartial(model, retn);
            return retn;
        }

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

    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 = ,
            };
            MapperToPartial(model, retn);
            return retn;
        }
    }
}
#pragma warning restore

at last

The above code fully introduces the recently implemented functions. Finally, you can use my latest nuget package to experience:

<ItemGroup>
   <PackageReference Include="" Version="1.7.0" />
   <PackageReference Include="" Version="1.7.0" PrivateAssets="all" />
</ItemGroup>

If you are interested in the complete implementation, you can move to my GitHub repository. Welcome to star/vipwan/