最近粗浅的学习了下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) 思路的一些想法的更多相关文章

  1. 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之十三 || DTOs 对象映射使用,项目部署Windows+Linux完整版

    更新 很多小伙伴在用 IIS 发布的时候,总是会有一些问题,文章下边 #autoid-6-0-0 我也简单的动图展示了,如何 publish 到 IIS 的过程,如果你能看懂,却发现自己的项目有问题的 ...

  2. 【道德经】漫谈实体、对象、DTO及AutoMapper的使用

    写在前面 实体和值对象 实体和对象 故常无欲以观其妙,常有欲以观其徼 初始实体和演化实体 代码中的DTO AutoMapper实体转换 后记 实体(Entity).对象(Object).DTO(Dat ...

  3. ASP.NET MVC 模型和数据对象映射实践

    在使用 MVC 开发项目的过程中遇到了个问题,就是模型和数据实体之间的如何快捷的转换?是不是可以像 Entity Framework 的那样 EntityTypeConfiguration,或者只需要 ...

  4. .NET自动化对象映射

    对象自动映射工具是用来解决对象之间映射转换的类库,能很好地解决DTO和Model之间的相互映射赋值问题. 只要两个对象的属性具有相同名字(或者符合它规定的命名约定),对象自动映射工具就可以替我们自动在 ...

  5. ASP.NET CORE 中使用AutoMapper进行对象映射

    ASP.NET CORE 中使用AutoMapper进行对象映射 1.什么是AutoMapper? AutoMapper是基于对象到对象约定的映射工具,常用于(但并不仅限制于)把复杂的对象模型转为DT ...

  6. C# AutoMapper:流行的对象映射框架,可减少大量硬编码,很小巧灵活,性能表现也可接受。

    AutoMapper 是一个对象-对象映射器,可以将一个对象映射到另一个对象. 官网地址:http://automapper.org/ 官方文档:https://docs.automapper.org ...

  7. 对象映射 - Mapping.Mapster

    前言 在项目中我们会经常遇到对象的映射,比如像Model和Dto之间的映射,或者是对象的深拷贝,这些都是需要我们自己实现的.此时,项目中会出现很多初始化对象的代码,这些代码写起来相当的枯燥乏味,那么有 ...

  8. 对象映射工具AutoMapper介绍

    AutoMapper是用来解决对象之间映射转换的类库.对于我们开发人员来说,写对象之间互相转换的代码是一件极其浪费生命的事情,AutoMapper能够帮助我们节省不少时间. 一. AutoMapper ...

  9. EF架构~AutoMapper对象映射工具简化了实体赋值的过程

    回到目录 AutoMapper是一个.NET的对象映射工具,一般地,我们进行面向服务的开发时,都会涉及到DTO的概念,即数据传输对象,而为了减少系统的负载,一般我们不会把整个表的字段作为传输的数据,而 ...

随机推荐

  1. Android中的数据存储(二):文件存储 2017-05-25 08:16 35人阅读 评论(0) 收藏

    文件存储 这是本人(菜鸟)学习android数据存储时接触的有关文件存储的知识以及本人自己写的简单地demo,为初学者学习和使用文件存储提供一些帮助.. 如果有需要查看SharedPreference ...

  2. 搭建MSSM框架(Maven+Spring+Spring MVC+MyBatis)

    https://github.com/easonjim/ssm-framework 先欠着,后续再进行讲解: 一.Spring内核集成 二.Spring MVC集成 三.MyBatis集成 四.代码生 ...

  3. CMOS DACs act as digitally controlled voltage dividers

    Digital potentiometers, such as Analog Devices’ AD5160, make excellent digitally controlled voltage ...

  4. IDA设置函数类型

    http://www.2cto.com/shouce/ida/1361.htm Action name: SetType 该命令允许你指定当前条目类型. 如果光标处在函数内部,那么函数类型将会被编辑, ...

  5. vue项目条形码和二维码生成工具试用

    项目开发需要,优惠券分不同类型,简单的使用id生成条形码供店铺使用,麻烦点的需要多个字段的就需要使用二维码来展示了,对应的效果如下 条形码(一维码)使用工具code128 需引入code128.js ...

  6. windows端口

    端口可分为3大类: 1) 公认端口(Well Known Ports):从0到1023,它们紧密绑定于一些服务.通常这些端口的通讯明确表明了某种服务的协议.例如:80端口实际上总是HTTP通讯. 2) ...

  7. JSP如何导入ckeditor

    <textarea rows="3" cols="100" id="editor1"></textarea> < ...

  8. 一致性hash在分布式系统中的应用

    场景 如果要设计一套KV存储的系统,用户PUT一个key和value,存储到系统中,并且提供用户根据key来GET对应的value.要求随着用户规模变大,系统是可以水平扩展的,主要要解决以下几个问题. ...

  9. WCF项目中出现“目标程序集不包含服务类型”的解决办法

    如果创建新项目时(以下简称A项目)选择的是WCF相关的项目模板,并且在A项目中只定义接口而不实现接口,那么任何引用了A项目的项目,在调试时都会弹出警告框“目标程序集不包含服务类型.可能需要调整此程序集 ...

  10. C#好书盘点

    C#是学习asp.net和.NET Winform程序的基础,所以一定要学好.从00年C#出道到现在,7年过去了,C#的书出了许多,有不少好书. 1.<21天学通C# > 作者:作者:(美 ...