利用源生成器实现对象映射,在编译阶段生成映射代码,减少运行时反射

利用源生成器,在编译阶段生成映射代码,减少运行时反射

这里有一个MappingTestModel类和MappingTestModelDto类,实现对象自身的拷贝,或者MappingTestModel映射MappingTestModelDto

GenMapperAttribute标注了类型需要生成映射方法,同时实现IAutoMap接口(由生成器实现接口, 类型转换时可以用obj is IAutoMap map检查)

构造函数可选参数为目标类型,默认是自身

MapBetweenAttribute用于自定义转换动作,支持定义在类上或者属性上,两者的有一下区别

  • 定义在类上时,必须使用3个参数的构造函数,并且支持单对多,多对单的映射
  • 定义在属性上,必须使用2个参数的构造函数,并且只支持单对单映射
[GenMapper(typeof(MappingTestModelDto))]
[MapBetween(typeof(MappingTestModelDto), ["Id", "Name", "Level"], "Label", By = nameof(FormatLabel))]
public partial class MappingTestModel
{
    public int Id { get; set; }
    [MapBetween(typeof(MappingTestModelDto), nameof(MappingTestModelDto.DisplayName))]
    public string Name { get; set; }
    public string Level { get; set; }
    public DateTime Deadline { get; set; }
    [MapBetween(typeof(MappingTestModelDto), nameof(Last),By = nameof(DTL))]
    public DateTime Last { get; set; }

    private static long DTL(DateTime dt)
    {
        return dt.Ticks;
    }

    private static DateTime DTL(long tick)
    {
        return new DateTime(tick);
    }

    private static string FormatLabel(int id, string name, string level)
    {
        return $"{id + 1}-{name}-{level}";
    }
    //可选反向映射方法
    private static (int, string, string) FormatLabel(string label)
    {
        var values = label.Split('-');
        return (int.Parse(values[0]), values[1], values[2]);
    }
}

public class MappingTestModelDto
{
    public string Id { get; set; }
    public string DisplayName { get; set; }
    public int Level { get; set; }
    public string Deadline { get; set; }
    public long Last { get; set; }
    public string Label { get; set; }
}

对于MappingTestModel类型,生成器将生成如下代码

using AutoGenMapperGenerator;
// <auto-generated/>
#pragma warning disable
#nullable enable
namespace TestProject1.Models
{
    [global::System.CodeDom.Compiler.GeneratedCode("AutoGenMapperGenerator.AutoMapperGenerator", "0.0.7.0")]
    /// <inheritdoc/>
    partial class MappingTestModel : AutoGenMapperGenerator.IAutoMap
    {
        [global::System.CodeDom.Compiler.GeneratedCode("AutoGenMapperGenerator.AutoMapperGenerator", "0.0.7.0")]
        public TestProject1.Models.MappingTestModelDto MapToMappingTestModelDto()
        {
            var _result_gen = new TestProject1.Models.MappingTestModelDto();
            _result_gen.Label = TestProject1.Models.MappingTestModel.FormatLabel(this.Id, this.Name, this.Level);
            _result_gen.Id = this.Id.ToString();
            _result_gen.DisplayName = this.Name;
            if (int.TryParse(this.Level.ToString(), out var _Level_out_gen))
            {
                _result_gen.Level = _Level_out_gen;
            }
            _result_gen.Deadline = this.Deadline.ToString();
            _result_gen.Last = TestProject1.Models.MappingTestModel.DTL(this.Last);
            return _result_gen;
        }

        [global::System.CodeDom.Compiler.GeneratedCode("AutoGenMapperGenerator.AutoMapperGenerator", "0.0.7.0")]
        public void MapFromMappingTestModelDto(TestProject1.Models.MappingTestModelDto _target_gen)
        {
            var _Label_arr_gen = TestProject1.Models.MappingTestModel.FormatLabel(_target_gen.Label);
            this.Id = _Label_arr_gen.Item1;
            this.Name = _Label_arr_gen.Item2;
            this.Level = _Label_arr_gen.Item3;
            if (int.TryParse(_target_gen.Id, out var _Id_out_gen))
            {
                this.Id = _Id_out_gen;
            }
            this.Name = _target_gen.DisplayName;
            this.Level = _target_gen.Level.ToString();
            if (System.DateTime.TryParse(_target_gen.Deadline, out var _Deadline_out_gen))
            {
                this.Deadline = _Deadline_out_gen;
            }
            this.Last = TestProject1.Models.MappingTestModel.DTL(_target_gen.Last);
        }

        [global::System.CodeDom.Compiler.GeneratedCode("AutoGenMapperGenerator.AutoMapperGenerator", "0.0.7.0")]
        public object? MapTo(string? target = null)
        {
            return MapToMappingTestModelDto();
        }

        [global::System.CodeDom.Compiler.GeneratedCode("AutoGenMapperGenerator.AutoMapperGenerator", "0.0.7.0")]
        public void MapFrom(object? value)
        {
            if (value is TestProject1.Models.MappingTestModelDto source)
            {
                MapFromMappingTestModelDto(source);
            }
        }
    }
}

因此,对于一个已有的MappingTestModel实例,你可以用如下方法获取MappingTestModelDto实例

var p = new MappingTestModel();
var pdto = p.MapToMappingTestModelDto();
// 或者使用IAutoMap接口
var dto2 = p.MapTo<MappingTestModelDto>(nameof(MappingTestModelDto))

总结

源生成器号称编译阶段的反射,性能上确定很有优势。但是目前配置的话确实不方便,需要在字段上进行Attribute标注,字段匹配规则也还没设置

感兴趣的可以看看源码生成器源码地址

posted @ 2024-08-23 10:14  yaoqinglin_mtiter  阅读(57)  评论(0)    收藏  举报