转载来源: https://www.cnblogs.com/zhouandke/p/16695968.html
接手的项目还在用 TinyMapper 的一个早期版本用来做自动映射工具,TinyMapper 虽然速度快,但在配置里不能转换类型,比如 deleted 在数据库中用 0、1 表示,转换成实体模型时没法转换成 bool 类型,就为了这一个属性,就必须手写代码人工转换(怪不得有些 Mapper 作者认为 TinyMapper 是一个 toy)。 于是试一试 AutoMapper,可是这货需要提前注册所有的映射关系,程序员本来就已经很累了。。。(最新版 TinyMapper 也要求提前注册所有映射关系)。 找出以前使用过的 ValueInjecter,可扩展性很强,使用反射来实现。虽然我认为对现在处理器性能而言,快慢已经不太重要了,但它速度实在太慢了,有些测试项目消耗时间是 json反序列化的一半,TinyMapper 和 AutoMapper 均使用 emit 实现,非常接近手写代码的速度了。 在 nuget.org 上找了找,还发现两个非常不错的 mapper: 1. UltraMapper 不需要提前注册映射关系,而且使用 ReferenceTracking 解决了循环问题。 2. HigLabo.Mapper 也不需要提前注册映射关系(看来牛人都对提前注册很不爽),支持 object 转换为Dictionary,提出了 PostAction 概念(自动映射出目标对象后,还可以执行自定义动作进行手工赋值,这样就不需要费力实现 Flattening 什么的了)。但试用过程中,发现不能实现 Array 到 List 的转换,而且作者也不打算改。。。 既然各个 Mapper 都不太顺手,并且这段时间疫情封控,于是决定自己手撸一个POCO的 Mapper,目标如下: 1. 不需要提前注册映射关系 2. 像 json序列化/反序列化一样,同名属性尽可能映射(比如 int? 到 enum) 3. 增加 HigLabo.Mapper的PostAction概念 4. 使用 表达式树/Emit 提高速度
编写过程中参考了 TinyMapper 和UltraMapper的代码,使用示例:
代码语言:javascript复制 1 public class Person
2 {
3 public int Id { get; set; }
4
5 public string Name { get; set; }
6 // Same Name, different type
7 public byte? Age { get; set; }
8 }
9
10 public class PersonDto
11 {
12 public int Id { get; set; }
13
14 public string Name { get; set; }
15
16 public int? Age { get; set; }
17
18 public string Description { get; set; }
19 }
20
21 class Program
22 {
23 static void Main(string[] args)
24 {
25 // register PostAction
26 ZK.Mapper.SimpleMapper.Default.SetPostAction<Person, PersonDto>((p, dto) =>
27 {
28 if (dto == null)
29 {
30 return dto;
31 }
32 dto.Description = p.Age.HasValue ? $"{p.Name} age is {p.Age}" : $"{p.Name} age is unknown";
33 return dto;
34 });
35
36 var p = new Person()
37 {
38 Id = 1,
39 Name = "john",
40 Age = 32
41 };
42
43 var dto = ZK.Mapper.SimpleMapper.Default.Map<PersonDto>(p);
44 Console.WriteLine(dto.Description);
45 }
46 }
写写这里面的总结吧 1. 内部Mapper都是泛型的,但使用时传入的source很可能是 object,所以都是使用 反射创建泛型化的Mapper实例,然后建立TypePair的对应关系,这样就解偶了泛型 2. Emit 和 表达式树原理都是一样的,建立IL代码,所以效率非常接近 3. 如果能像 AutoMapper 那样提前注册所有映射关系,速度优化的手段会更多,估计这也是 TinyMapper 转成提前注册的原因吧。很多 Mapper 的性能测试都号称比 AutoMapper 快,但引用的都是老版本的 AutoMapper,但现在 AutoMapper 非常快,在一些简单测试里赶上了 TinyMapper。如果添加了很多特色功能,却很拖累速度。当然我还是觉得只要不是数量级的差距,都不太重要。 4. 我的潜意识里 SimpleMapper 就为解决当前项目的问题,比如从数据库中读出来对象,映射成Dto后,就不会被再使用了,所以SimpleMapper默认是浅拷贝。所以我也不打算发布到nuget。 5. SimpleMapper 功能不多,也没有为性能做太多优化,所以结构比较清晰,有兴趣的朋友阅读起来应该不难。
SimpleMapper 代码地址