关于对象映射(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的概念,即数据传输对象,而为了减少系统的负载,一般我们不会把整个表的字段作为传输的数据,而 ...
随机推荐
- Redis-用思维导图二天搞定Redis用法。
Redis整体面貌 Redis基本数据结构 1.String 1.1 数据结构 long len byte数组长度 long free 可用数组长度 char buff[] 数据内容 1.2 命令 键 ...
- StoryBoard学习(5):使用segue页面间传递数据
StoryBoard学习(5):使用segue页面间传递数据 函数: - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sen ...
- windows系统上安装与使用Android NDK r8d(一)
什么是NDK? NDK 提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so 和 java 应用一起打包成apk.这些工具对开发者的帮助是巨大的. NDK 集成了交叉编译器,并 ...
- redis cluster 设置密码做集群时gem下client.rb文件修改
redis节点有设置密码,然后在创建集群的时候没有设置密码的命令 ./redis-trib.rb create --replicas 1 127.0.0.1:6381 127.0.0.1:6382 1 ...
- andriod 下一个页面
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); s ...
- 淘宝API开发第一步
1.登录淘宝开放平台:http://open.taobao.com/ 2.添加网站 (验证完网站后,会提醒“JSSDK以激活提交审核按钮”,这个需要的UV达100,按钮才会亮,审核过程中也得保持UV的 ...
- Appium+python自动化22-Appium Desktop
Appium Desktop 原滋原味的官方文档 Appium Desktop是一款用于Mac.Windows和Linux的开源应用,它提供了Appium自动化服务器在一个漂亮灵活的UI中的强大功能. ...
- LaTex 制作表格 合并行\multirow 合并列\multicolumn
在latex文件最前面用这个包\usepackage{multirow} multirow 宏包提供了 \multirow 命令可以在表格中排版横跨两行以上的文本.命令的格式如下: \multirow ...
- 一个table插件,用于bootstrap开发
最近项目中改用bootstrap,可以给的通用table,写的有点死,id名称是固定的,那一个页面两个table如何做呢? ok,模仿着别人的代码,写了一个,整体代码如下: ; (function(f ...
- Java项目在jsp页面中引入jquery框架的步骤
环境:在Java web项目中引入juqery框架 工具:MyEclipse8.5 [步骤如下] A:新建一个Java web项目TestJquery,在WebRoot目录下创建一个jquery文件 ...