关于对象映射(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的概念,即数据传输对象,而为了减少系统的负载,一般我们不会把整个表的字段作为传输的数据,而 ...
随机推荐
- SSE两个页面的相互通信
两个页面之间互相通信 首先搭建express框架,然后通过two页面发送数据给服务器,服务器把数据传送给one页面 在two 中发送数据,在one中显示 router/index.js var axi ...
- Linux下Shell命令的输出信息同时显示在屏幕和保存到日志文件中
#直接覆盖日志文件 ls -l | tee ./t.log #将输出内容附加到日志文件 ls -l | tee -a ./t.log 使用的是tee命令
- Linux下使用ISC DHCP可以实现动态推送静态路由表
ISC DHCP可以实现动态推送静态路由表,先做个记号. 参考: https://gauvain.pocentek.net/docs/dhcpd-push-routes/ http://www.isc ...
- HDU 4679 Terrorist’s destroy (2013多校8 1004题 树形DP)
Terrorist’s destroy Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Othe ...
- Generate stabilized PWM signals
A standard technique for generating analog voltages using µCs is to use a PWM output and filter the ...
- REDIS 配制
http://www.cnblogs.com/hoojo/p/4466024.html
- jdbc框架有很多,包括spring jdbc
1.由于jdbc连接的繁琐性,故很多公司封装了jdbc框架,比如spring jdbc 2.比如spring jdbc框架中,用jdbctemplate, 通过jdbcTemplate 提供 int ...
- VisualStudio:WEB 性能测试和负载测试 入门
背景 一直做的是中小企业应用,很少关注性能和负载这里,进来准备看一本关于并发编程的图书,为了量化的测试 WEB 环境的性能和负载,特意玩了一下 VS 提供的测试项目. 新的测试项目 新建项目 性能测试 ...
- appium+python自动化57-chromedriver与chrome版本
前言 由于app的webview自动化是依赖于chromedriver的,并且每个app的webview版本号都不太一样,这就导致了,每次都需要重新去下载对应的chromedriver版本.如何根据当 ...
- signal
//signaltest.c // 子线程阻塞,等待信号,然后输出字符串 // 主线程从键盘录入字符,给子线程发信号. #include <stdio.h> #include <un ...