关于对象映射(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的概念,即数据传输对象,而为了减少系统的负载,一般我们不会把整个表的字段作为传输的数据,而 ...
随机推荐
- Android中的数据存储(二):文件存储 2017-05-25 08:16 35人阅读 评论(0) 收藏
文件存储 这是本人(菜鸟)学习android数据存储时接触的有关文件存储的知识以及本人自己写的简单地demo,为初学者学习和使用文件存储提供一些帮助.. 如果有需要查看SharedPreference ...
- 搭建MSSM框架(Maven+Spring+Spring MVC+MyBatis)
https://github.com/easonjim/ssm-framework 先欠着,后续再进行讲解: 一.Spring内核集成 二.Spring MVC集成 三.MyBatis集成 四.代码生 ...
- CMOS DACs act as digitally controlled voltage dividers
Digital potentiometers, such as Analog Devices’ AD5160, make excellent digitally controlled voltage ...
- IDA设置函数类型
http://www.2cto.com/shouce/ida/1361.htm Action name: SetType 该命令允许你指定当前条目类型. 如果光标处在函数内部,那么函数类型将会被编辑, ...
- vue项目条形码和二维码生成工具试用
项目开发需要,优惠券分不同类型,简单的使用id生成条形码供店铺使用,麻烦点的需要多个字段的就需要使用二维码来展示了,对应的效果如下 条形码(一维码)使用工具code128 需引入code128.js ...
- windows端口
端口可分为3大类: 1) 公认端口(Well Known Ports):从0到1023,它们紧密绑定于一些服务.通常这些端口的通讯明确表明了某种服务的协议.例如:80端口实际上总是HTTP通讯. 2) ...
- JSP如何导入ckeditor
<textarea rows="3" cols="100" id="editor1"></textarea> < ...
- 一致性hash在分布式系统中的应用
场景 如果要设计一套KV存储的系统,用户PUT一个key和value,存储到系统中,并且提供用户根据key来GET对应的value.要求随着用户规模变大,系统是可以水平扩展的,主要要解决以下几个问题. ...
- WCF项目中出现“目标程序集不包含服务类型”的解决办法
如果创建新项目时(以下简称A项目)选择的是WCF相关的项目模板,并且在A项目中只定义接口而不实现接口,那么任何引用了A项目的项目,在调试时都会弹出警告框“目标程序集不包含服务类型.可能需要调整此程序集 ...
- C#好书盘点
C#是学习asp.net和.NET Winform程序的基础,所以一定要学好.从00年C#出道到现在,7年过去了,C#的书出了许多,有不少好书. 1.<21天学通C# > 作者:作者:(美 ...