关于对象映射(Dto->model) 思路的一些想法
最近粗浅的学习了下AutoMapper 这个做对象映射的第三方工具,觉得非常方便使用,所以简单的总结了一下我能想到的简单的对象映射的方式。
占时先不考虑源对象成员到目标对象成员的指定映射(即成员名不一致),先准备好两个类Students-StudentsDto;Teachers-TeachersDto

1 public class Students
2 {
3 public int No { get; set; }
4 public string Name { get; set; }
5 public bool Gender { get; set; }
6 public string Class { get; set; }
7
8 public string _remark;
9 }
10
11 public class StudentsDto
12 {
13 public int No { get; set; }
14 public string Name { get; set; }
15 public bool Gender { get; set; }
16 public string Class { get; set; }
17
18 public string _remark;
19 }
20
21 public class Teachers
22 {
23 public int No { get; set; }
24
25 public string Course { get; set; }
26
27 public string Name { get; set; }
28 }
29
30 public class TeachersDto
31 {
32 public int No { get; set; }
33
34 public string Course { get; set; }
35
36 public string Name { get; set; }
37 }

我们先使用普通的对象装载方式:

1 StudentsDto studentsDto = new StudentsDto { No = 1, Name = "Epic", Gender = true, Class = "家里蹲一班", _remark = "逗比" };
2 TeachersDto teachersDto = new TeachersDto { No = 2, Name = "Eleven", Course = ".net" };
3 Students students = new Students
4 {
5 No = studentsDto.No,
6 Name = studentsDto.Name,
7 Gender = studentsDto.Gender,
8 Class = studentsDto.Class,
9 _remark = studentsDto._remark
10 };
11 Teachers teachers = new Teachers
12 {
13 No = teachersDto.No,
14 Name = teachersDto.Name,
15 Course = teachersDto.Course
16 };

总结:其思路无非就是先new一个对象实例,然后将该实例的成员一一赋值。
1.通过反射的方式来实现对象映射,是我们最容易想到的方式,思路也很简单

1 public static TModel Trans<TModel, TModelDto>(TModelDto dto)
2 where TModel : class
3 where TModelDto : class
4 {
5 TModel model = Activator.CreateInstance(typeof(TModel)) as TModel;
6 //获取TModel的属性集合
7 PropertyInfo[] modlePropertys = typeof(TModel).GetProperties();
8 //获取TModelDto的属性集合
9 Type type = dto.GetType();
10 PropertyInfo[] propertys = type.GetProperties();
11 foreach (var property in propertys)
12 {
13 foreach (var mproperty in modlePropertys)
14 {
15 //如果属性名称一致,则将该属性值赋值到TModel实例中
16 //这里可以用Attribute来实现成员的自定义映射
17 if (property.Name.Equals(mproperty.Name))
18 {
19 mproperty.SetValue(model, property.GetValue(dto));
20 break;
21 }
22 }
23 }
24
25 //获取TModel的字段集合
26 FieldInfo[] modelfieldInfos = typeof(TModel).GetFields();
27 //获取TModelDto的字段集合
28 FieldInfo[] fieldInfos = type.GetFields();
29 foreach (var field in fieldInfos)
30 {
31 foreach (var mfield in modelfieldInfos)
32 {
33 //如果字段名称一致,则将该字段值赋值到TModel实例中
34 if (field.Name.Equals(mfield.Name))
35 {
36 mfield.SetValue(model, field.GetValue(dto));
37 break;
38 }
39 }
40 }
41 return model;
42 }

总结:通过反射来创建对象实例,然后将实例成语的值通过反射的方式获取并赋值。
2.通过序列号的方式,对象类型可以转换成json字符串,然后再由json字符串转换成所需的对象不就可以了么

1 public static TModel Trans<TModel, TModelDto>(TModelDto dto)
2 where TModel : class
3 where TModelDto : class
4 {
5 return JsonConvert.DeserializeObject<TModel>(JsonConvert.SerializeObject(dto));
6 }

总结:通过序列号然后反序列化,这样使用感觉并不是Newtonsoft.Json的初衷,不知道性能到底如何呢?
3.使用Expression表达式的方式来解决,将所需实例对象new、赋值的过程先写入表达式,然后生成lambda表达式,最后编译该表达式生成委托,invoke即可

1 public static class ExpressionAndSeesionMethod
2
3 {
4 public static Dictionary<string, object> _dictionary = new Dictionary<string, object>();
5
6 public static TModel Trans<TModel, TModelDto>(TModelDto dto)
7 {
8 Type modelType = typeof(TModel);
9 Type modelDtoType = typeof(TModelDto);
10
11 //如果_dictionary中不存在该key,则存进去
12 string key = $"{modelDtoType.Name}-->{modelType.Name}";
13 if (!_dictionary.ContainsKey(key))
14 {
15 //创建一个lambda参数x,定义的对象为TModelDto
16 ParameterExpression parameterExpression = Expression.Parameter(modelDtoType, "x");
17 //开始生成lambda表达式
18 List<MemberBinding> list = new List<MemberBinding>();
19 foreach (var item in modelType.GetProperties())
20 {
21 //为x参数表达式生成一个属性值
22 MemberExpression property = Expression.Property(parameterExpression, modelDtoType.GetProperty(item.Name));
23 //将该属性初始化 eg:No=x.No
24 MemberBinding memberBinding = Expression.Bind(item, property);
25 list.Add(memberBinding);
26 }
27
28 foreach (var item in typeof(TModel).GetFields())
29 {
30 //为x参数表达式生成一个字段值
31 MemberExpression field = Expression.Field(parameterExpression, modelDtoType.GetField(item.Name));
32 //将该字段初始化
33 MemberBinding memberBinding = Expression.Bind(item, field);
34 list.Add(memberBinding);
35 }
36 //调用构造函数,初始化一个TModel eg: new{No=x.No...}
37 MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(modelType), list);
38 //创建lambda表达式 eg: x=>new{ No=x.No...}
39 Expression<Func<TModelDto, TModel>> lambda = Expression.Lambda<Func<TModelDto, TModel>>(memberInitExpression, parameterExpression);
40 //将lambda表达式生成委托
41 Func<TModelDto, TModel> func = lambda.Compile();
42 _dictionary[key] = func;
43 }
44 return ((Func<TModelDto, TModel>)_dictionary[key]).Invoke(dto);
45 }
46 }

总结:使用表达式树的方式,可以将生成的委托保存起来,这里使用dictionary字典,在需要使用的时候调用即可(一次生成委托,后续可多次使用);既然是多个委托,那是不是可以使用的泛型委托Func<TIn,TResult>呢?
使用泛型缓存:

1 /// <summary>
2 ///泛型委托基于泛型类之上
3 ///泛型静态类在确定参数类型的时候会调用其静态函数
4 ///在执行委托时,泛型委托会内置查找相应的委托来执行
5 /// </summary>
6 public static class ExpressionAndFuncMethod<TModel, TModelDto>
7 where TModel : class
8 where TModelDto : class
9 {
10 static ExpressionAndFuncMethod()
11 {
12 ExpressionMapper();
13 }
14
15 public static Func<TModelDto, TModel> _func = null;
16
17 public static void ExpressionMapper()
18 {
19 Type modelType = typeof(TModel);
20 Type modelDtoType = typeof(TModelDto);
21
22 //创建一个lambda参数x,定义的对象为TModelDto
23 ParameterExpression parameterExpression = Expression.Parameter(modelDtoType, "x");
24 //开始生成lambda表达式
25 List<MemberBinding> list = new List<MemberBinding>();
26 foreach (var item in modelType.GetProperties())
27 {
28 //为x参数表达式生成一个属性值
29 MemberExpression property = Expression.Property(parameterExpression, modelDtoType.GetProperty(item.Name));
30 //将该属性初始化 eg:No=x.No
31 MemberBinding memberBinding = Expression.Bind(item, property);
32 list.Add(memberBinding);
33 }
34
35 foreach (var item in typeof(TModel).GetFields())
36 {
37 //为x参数表达式生成一个字段值
38 MemberExpression field = Expression.Field(parameterExpression, modelDtoType.GetField(item.Name));
39 //将该字段初始化
40 MemberBinding memberBinding = Expression.Bind(item, field);
41 list.Add(memberBinding);
42 }
43 //调用构造函数,初始化一个TModel eg: new{No=x.No...}
44 MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(modelType), list);
45 //创建lambda表达式 eg: x=>new{ No=x.No...}
46 Expression<Func<TModelDto, TModel>> lambda = Expression.Lambda<Func<TModelDto, TModel>>(memberInitExpression, parameterExpression);
47 //将lambda表达式生成委托
48 _func = lambda.Compile();
49 }
50
51 public static TModel Trans(TModelDto dto)
52 {
53 if (_func != null)
54 return _func(dto);
55 return default(TModel);
56 }
57 }

总结:使用泛型委托时,即用到了其泛型缓存,在调用指定参数的委托时,能快速查找并调用(这个性能应该是高于使用字典的查找)
4.使用AutoMapper

1 public static class AutoMapperMethod
2 {
3 /// <summary>
4 /// AutoMapper 必须先创建映射
5 /// </summary>
6 /// <param name="dictionary"></param>
7 public static void Init(Dictionary<Type, Type> dictionary)
8 {
9 AutoMapper.Mapper.Initialize(x =>
10 {
11 foreach (var item in dictionary)
12 {
13 x.CreateMap(item.Key, item.Value);
14 }
15 });
16 }
17
18
19 public static TModel Trans<TModel, TModelDto>(TModelDto dto)
20 where TModel : class
21 where TModelDto : class
22 {
23 return AutoMapper.Mapper.Map<TModelDto, TModel>(dto);
24 }
25 }

总结:AutoMapper先创建再调用的原则,非常适合core项目的 注册-调用 思想,在Configure中进行注册,然后使用时Map即可,AutoMap使用emit代码开发(不太明白),性能很好,是现在最流行的映射工具。
最后,来看一下以上几种方式的性能对比吧,测试条件是将StudentsDto实例转换成Students实例,将TeachersDto实例转换成Teachers实例,各转换50万次,耗时如下图:

从上往下依次是:普通类型装载、反射、序列化、表达式缓存、表达式泛型缓存、AutoMapper
由此可见,反射和序列化是比较慢的,表达式和AutoMapper的表现差不多,一般项目中,类型映射的次数也不会很大,使用AutoMapper就已经非常够用了。
本人不才,还希望园内技术牛人多多指正。
关于对象映射(Dto->model) 思路的一些想法的更多相关文章
- 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之十三 || DTOs 对象映射使用,项目部署Windows+Linux完整版
更新 很多小伙伴在用 IIS 发布的时候,总是会有一些问题,文章下边 #autoid-6-0-0 我也简单的动图展示了,如何 publish 到 IIS 的过程,如果你能看懂,却发现自己的项目有问题的 ...
- 【道德经】漫谈实体、对象、DTO及AutoMapper的使用
写在前面 实体和值对象 实体和对象 故常无欲以观其妙,常有欲以观其徼 初始实体和演化实体 代码中的DTO AutoMapper实体转换 后记 实体(Entity).对象(Object).DTO(Dat ...
- ASP.NET MVC 模型和数据对象映射实践
在使用 MVC 开发项目的过程中遇到了个问题,就是模型和数据实体之间的如何快捷的转换?是不是可以像 Entity Framework 的那样 EntityTypeConfiguration,或者只需要 ...
- .NET自动化对象映射
对象自动映射工具是用来解决对象之间映射转换的类库,能很好地解决DTO和Model之间的相互映射赋值问题. 只要两个对象的属性具有相同名字(或者符合它规定的命名约定),对象自动映射工具就可以替我们自动在 ...
- ASP.NET CORE 中使用AutoMapper进行对象映射
ASP.NET CORE 中使用AutoMapper进行对象映射 1.什么是AutoMapper? AutoMapper是基于对象到对象约定的映射工具,常用于(但并不仅限制于)把复杂的对象模型转为DT ...
- C# AutoMapper:流行的对象映射框架,可减少大量硬编码,很小巧灵活,性能表现也可接受。
AutoMapper 是一个对象-对象映射器,可以将一个对象映射到另一个对象. 官网地址:http://automapper.org/ 官方文档:https://docs.automapper.org ...
- 对象映射 - Mapping.Mapster
前言 在项目中我们会经常遇到对象的映射,比如像Model和Dto之间的映射,或者是对象的深拷贝,这些都是需要我们自己实现的.此时,项目中会出现很多初始化对象的代码,这些代码写起来相当的枯燥乏味,那么有 ...
- 对象映射工具AutoMapper介绍
AutoMapper是用来解决对象之间映射转换的类库.对于我们开发人员来说,写对象之间互相转换的代码是一件极其浪费生命的事情,AutoMapper能够帮助我们节省不少时间. 一. AutoMapper ...
- EF架构~AutoMapper对象映射工具简化了实体赋值的过程
回到目录 AutoMapper是一个.NET的对象映射工具,一般地,我们进行面向服务的开发时,都会涉及到DTO的概念,即数据传输对象,而为了减少系统的负载,一般我们不会把整个表的字段作为传输的数据,而 ...
随机推荐
- Trigger a TTL circuit from ECL levels
ECL circuits typically have relatively small logic spans of approximately 800 mV. Because of the sma ...
- java并发集合知识点(二)
我们平时写程序需要经常用到集合类,比如ArrayList.HashMap等,但是这些集合不能够实现并发运行机制,这样在服务器上运行时就会非常的消耗资源和浪费时间,并且对这些集合进行迭代的过程中不能进行 ...
- 微信公众平台——获取access_token、expires_in
微信公众平台——获取access_token.expires_in 在微信公众平台接口开发中,Access Token占据着重要地位,它相当于进入各种接口的邀请,拿到这个钥匙才拥有调用其他各种特殊接口 ...
- 调用 jdbcTemplate.queryForList 时出现错误 spring-org.springframework.jdbc.IncorrectResultSetColumnCountException
国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私 ...
- Xcode 安装插件管理器
https://github.com/alcatraz/Alcatraz运行之后, load bundle, 然后window就有pakagegemanage. 下载如下插件: 自动导入插件
- openssl https 单向认证连接成功示例
研究这个玩意也有几天的时间了,刚学C 因为不熟悉编译折腾了不少时间,终于弄通了,发个随笔给研究openssl https的同学一点提示吧. 环境: ========================== ...
- OpenCV学习(3) OpenCV框架
OpenCV是一个开源的视觉库,其中包括很多计算机视觉的算法实现.在版本2.2以后,OpenCV采用C++特征的API,在1.x版本中,OpenCV函数都是传统的C语言形式. ...
- C++构造函数、析构函数、虚析构函数
1.构造函数 C++中的构造函数是用于初始化类的各种变量以及分配资源等.主要的注意事项是: (1)在继承关系中先初始化父类对象后初始化子类对象. (2)在一个类中按照变量的声明顺序,对类中的变量进行初 ...
- 高性能HTML
避免使用Iframe Iframe也叫内联frame,可以把一个HTML文档嵌入到另一个文档中.使用iframe的好处是被嵌入的文档可以完全独立于其父文档,凭借此特点我们通常可以使浏览器模拟多线程,需 ...
- Java 中 方法名或类名 变更 同时 更新 所有引用的 类名或方法名 的解决方案
选中 类名,或属性名 Ctrl + 1 然后选择 理新当前文件,还是更新整个工作空间,然后修改对应的类名或方法名 回车即可. 如果.有SVN 版本在控制着,则 会提示,然后把对应的文件 锁定 再 ...