利用源生成器实现对象映射,在编译阶段生成映射代码,减少运行时反射
利用源生成器,在编译阶段生成映射代码,减少运行时反射
这里有一个Product类和ProductDto类,实现对象自身的拷贝,或者Product映射ProductDto
GenMapperAttribute
标注了类型需要生成映射方法,同时实现IAutoMap
接口(由生成器实现接口, 类型转换时可以用obj is IAutoMap map
检查)
构造函数可选参数为目标类型,默认是自身
MaoToAttribute
和MapFromAttribute
用于自定义转换动作,顾名思义,MapTo是用于当前类型转换到目标类型用的,MapFrom是在目标类型标注的,由当前类型调用
public interface IAutoMap
{
object MapTo(string? target = null);
}
[GenMapper]
[GenMapper(typeof(ProductDto))]
internal partial class Product
{
public int Id { get; set; }
public string? Name { get; set; }
public string? Category { get; set; }
[MapTo(Target = typeof(ProductDto), Name = nameof(ProductDto.Date))]
public DateTime? ProductDate { get; set; }
public IEnumerable<Product> Products { get; set; } = [];
}
internal class ProductDto
{
public static string NameMapFrom(Product p)
{
return $"{p.Category}-{p.Name}";
}
public int Id { get; set; }
[MapFrom(Source = typeof(Product), By = nameof(NameMapFrom))]
public string? Name { get; set; }
//[MapFrom(Source = typeof(Product), Name = nameof(Product.ProductDate))]
public DateTime? Date { get; set; }
}
对于Product类型,生成器将生成如下代码
// <auto-generated/>
#pragma warning disable
namespace TestProject1.Models
{
[global::System.CodeDom.Compiler.GeneratedCode("AutoGenMapperGenerator.AutoMapperGenerator", "1.0.0.0")]
/// <inheritdoc/>
partial class Product : IAutoMap
{
[global::System.CodeDom.Compiler.GeneratedCode("AutoGenMapperGenerator.AutoMapperGenerator", "1.0.0.0")]
public TestProject1.Models.Product MapToProduct()
{
var result = new TestProject1.Models.Product();
result.Id = this.Id;
result.Name = this.Name;
result.Category = this.Category;
result.ProductDate = this.ProductDate;
result.Products = this.Products.Select(i => i.MapTo<TestProject1.Models.Product>("Product")).ToList();
return result;
}
[global::System.CodeDom.Compiler.GeneratedCode("AutoGenMapperGenerator.AutoMapperGenerator", "1.0.0.0")]
public TestProject1.Models.ProductDto MapToProductDto()
{
var result = new TestProject1.Models.ProductDto();
result.Date = this.ProductDate;
result.Id = this.Id;
result.Name = TestProject1.Models.ProductDto.NameMapFrom(this);
return result;
}
[global::System.CodeDom.Compiler.GeneratedCode("AutoGenMapperGenerator.AutoMapperGenerator", "1.0.0.0")]
public object? MapTo(string? target = null)
{
if (string.IsNullOrEmpty(target))
throw new ArgumentNullException(nameof(target), "存在多个目标类型,请指定目标类型,推荐使用nameof(TargetType)");
if (target == nameof(Product))
return MapToProduct();
if (target == nameof(ProductDto))
return MapToProductDto();
throw new ArgumentException("未找到指定目标类型的映射方法");
}
}
}
因此,对于一个已有的Product实例,你可以用如下方法获取Product新实例和ProductDto实例
var p = new Product();
var p1 = p.MapToProduct();
var pdto = p.MapToProductDto();
// 或者使用IAutoMap接口
var p2 = p.MapTo<Product>(nameof(Product));
var dto2 = p.MapTo<ProductDto>(nameof(ProductDto))
总结
源生成器号称编译阶段的反射,性能上确定很有优势。但是目前配置的话确实不方便,需要在字段上进行Attribute标注,字段匹配规则也还没设置
感兴趣的可以看看源码生成器源码地址