关于对象映射(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的概念,即数据传输对象,而为了减少系统的负载,一般我们不会把整个表的字段作为传输的数据,而 ...
随机推荐
- DropdownList 赋初始值问题
网上查了这样的代码 虽然是可以用.但是会点击多次会出现”“ dropdownList不能选多个值的问题“ private void initdroplistitemlirun(string c_Bus ...
- php curl 抓取
<?php set_time_limit(0); function curl_multi($urls) { if (!is_array($urls) or count($urls) == 0) ...
- Java代码优化方案 J2ME内存优化
Java代码优化方案 J2ME内存优化 从几本书上,N个网站上整理的一些JAVA代码优化方案,最近的项目只有1M内存可用,必须很抠门了~J2ME项目更要注意的 避免内存溢出 l 不用的对象释放(置空) ...
- iTunes Connect App Bundles
App Bundles捆绑销售提交流程: 1. 在iTunes Connect左上「+」选「Create Bundle」到「New App Bundle」挑选已上线应用(最多可捆绑10个应用) 2. ...
- Android BottomNavigationBar底部导航控制器的使用
最近Google在自己推出的Material design中增加了Bottom Navigation导航控制.Android一直没有官方的导航控制器,自己实现确实是五花八门,有了这个规定之后,就类似苹 ...
- llvm中如何利用分支概率和基本块频率估计
1. 背景 llvm自2.9版以后,已经集成了对分支概率和基本块频率的静态分析. 分支概率(branch probability)是指在程序的控制流图中,从控制流从一个基本块A到其任意后继基本块Si的 ...
- c#抓去网页
c#利用WebClient和WebRequest获取网页源代码的比较 2011-11-28 10:26:42 我来说两句 收藏 我要投稿 C#中一般是可以利用WebClient类和WebReq ...
- Spark开源学习模块
Spark开源的各模块组成结构: 1. Narrow dependency--窄依赖,子RDD依赖于父RDD中固定的data partition. 2. Stage--分为多个阶段进行作业. 3. W ...
- 折叠伸缩工具栏 CollapsingToolbarLayout
PS:这是一个超级超级垃圾的控件,强烈建议放弃使用! demo地址:https://github.com/baiqiantao/CollapsingDemo.git 一个类似的效果的库,有800个星 ...
- 几个很实用的BOM属性对象方法
location对象 location.href-- 返回或设置当前文档的URLlocation.search -- 返回URL中的查询字符串部分.例如 http://www.dreamdu.com/ ...