关于对象映射(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的概念,即数据传输对象,而为了减少系统的负载,一般我们不会把整个表的字段作为传输的数据,而 ...
随机推荐
- HDU 4686 Arc of Dream (2013多校9 1001 题,矩阵)
Arc of Dream Time Limit: 2000/2000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others)Tota ...
- SpringMVC访问静态页面
Spring MVC显示静态页面 在前面搭建spring MVC环境时,我们设置了spring-mvc配置,通过tomcat来访问了index.jsp 页面,但是当我将页面换成.thml的静态面之后就 ...
- Debian 安装记录
1.蓝色标注是安装的部分或配置的. 作者:http://www.cppblog.com/jinglexy上海体育馆 2.linux 发行版测评网站:www.distrowatch.com 打 ...
- springBoot 2.X-自定义拦截器
package com.cx.springboot.myInter; import javax.servlet.http.HttpServletRequest; import javax.servle ...
- FIS常用命令
命令 用途 简写 fis --version 查看版本 fis -v fis install 安装 fis release 发布项目 fis server start 启动一个服务器用于预览项 ...
- KVM-Introduce
相信非常多的人对虚拟机并不陌生,眼下也有非常多优秀的虚拟机软件,比如:VMware, VirtualBox, Xen, KVM等.而本文的主要内容是介绍KVM. KVM: Kernel Based V ...
- rsync进行不同服务器之间的数据同步
2台服务器上都要安装rsync,sudo yum install rsync. 把远程的数据备份到本机: rsync -rP --rsh=ssh root@IP:/data/tmp /data/tmp ...
- STL中关联式容器的特性
1.map 代码如下: /* * map_1.cpp * * Created on: 2013年8月6日 * Author: Administrator */ #include <iostrea ...
- RV BaseRecyclerViewAdapterHelper 总结 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- JS中字符串的相关操作
一.字符串的创建 创建一个字符串有几种方法.最简单的是用引号将一组字符包含起来,可以将其赋值给一个字符串变量. var myStr = "Hello, String!"; 可以用双 ...