【AutoMapper官方文档】DTO与Domin Model相互转换(上)
原文: 【AutoMapper官方文档】DTO与Domin Model相互转换(上)

写在前面

  AutoMapper目录:

  • 【AutoMapper官方文档】DTO与Domin Model相互转换(上)
  • 【AutoMapper官方文档】DTO与Domin Model相互转换(中)
  • 【AutoMapper官方文档】DTO与Domin Model相互转换(下)
  • 未完待续。。。

  本篇目录:

  • Flattening-复杂到简单

  • Projection-简单到复杂

  • Configuration Validation-配置验证

  • Lists and Array-集合和数组

  • Nested mappings-嵌套映射

  • 后记

  上一篇《【道德经】漫谈实体、对象、DTO及AutoMapper的使用 》,因为内容写的有点跑偏,关于AutoMapper的使用最后只是简单写了下,很明显这种简单的使用方式不能满足项目中复杂的需要,网上找了下AutoMapper相关文档,但差不多都是像我一样简单的概述下,看来懒的不只有我一个,哈哈。在AutoMapper 官方文档中找到其使用的详细说明,天书一样的英文,然后就找相关中文文档,最后还是没找到,这边没办法,只能自己动手,丰衣足食了。英语牛逼的可以直接略过,查看英文文档,本篇也不算是翻译,因为本人英语实在拿不出手,只是按照示例加上自己的一些理解,做个学习笔记,不对的地方还请指正。

  注:虽然上一篇写跑偏了,但是本人真的很喜欢道德经,除了为人处世,在软件设计这方面其实也有体现,也希望可以运用到这上面,如果你和我有一样的想法,请在点击公告栏中的QQ链接,知音难觅啊!

Flattening-复杂到简单

  Flattening 翻译为压扁、拉平、扁平化的意思,可以理解为使原有复杂的结构变得简化,我们先看下领域模型和DTO代码:

 1     public class Order
 2     {
 3         private readonly IList<OrderLineItem> _orderLineItems = new List<OrderLineItem>();
 4         public Customer Customer { get; set; }
 5         public OrderLineItem[] GetOrderLineItems()
 6         {
 7             return _orderLineItems.ToArray();
 8         }
 9         public void AddOrderLineItem(Product product, int quantity)
10         {
11             _orderLineItems.Add(new OrderLineItem(product, quantity));
12         }
13         public decimal GetTotal()
14         {
15             return _orderLineItems.Sum(li => li.GetTotal());
16         }
17     }
18
19     public class Product
20     {
21         public decimal Price { get; set; }
22         public string Name { get; set; }
23     }
24
25     public class OrderLineItem
26     {
27         public OrderLineItem(Product product, int quantity)
28         {
29             Product = product;
30             Quantity = quantity;
31         }
32         public Product Product { get; private set; }
33         public int Quantity { get; private set; }
34         public decimal GetTotal()
35         {
36             return Quantity * Product.Price;
37         }
38     }
39
40     public class Customer
41     {
42         public string Name { get; set; }
43     }
44
45     public class OrderDto
46     {
47         public string CustomerName { get; set; }
48         public decimal Total { get; set; }
49     }

  可以看到领域模型 Order 是很复杂的,但是对于业务场景中的OrderDto却很简单,只有 CustomerName和Total两个属性,AutoMapper配置代码:

 1         public void Example()
 2         {
 3             var customer = new Customer
 4             {
 5                 Name = "George Costanza"
 6             };
 7             var order = new Order
 8             {
 9                 Customer = customer
10             };
11             var bosco = new Product
12             {
13                 Name = "Bosco",
14                 Price = 4.99m
15             };
16             order.AddOrderLineItem(bosco, 15);
17             // 配置 AutoMapper
18             Mapper.CreateMap<Order, OrderDto>();
19             // 执行 mapping
20             OrderDto dto = Mapper.Map<Order, OrderDto>(order);
21             Console.WriteLine("CustomerName:" + dto.CustomerName);
22             Console.WriteLine("Total:" + dto.Total);
23         }

  转换效果:

  可以看到配置相当的简单,只要设置下Order和OrderDto之间的类型映射就可以了,我们看OrderDto中的CustomerName和Total属性在领域模型Order中并没有与之相对性,没什么可以转换呢,感觉好神奇的样子,其实仔细发现这些属性的命名都有一定的规则,AutoMapper在做解析的时候会按照PascalCase(帕斯卡命名法),就是一种变量命名法,除了PascalCase还有Hungarian(匈牙利命名法)和camelCase(骆驼命名法),PascalCase就是指混合使用大小写字母来构成变量和函数的名字,首字母要大写,camelCase首字母小写,我们C#命名中,一般使用的是camelCase和PascalCase,比较高级的是PascalCase。

  但是为什么AutoMapper会解析Total呢?因为在领域模型Order中有个GetTotal()方法,AutoMapper会解析“Get”之后的单词,所以会与Total相对应,如果你把OrderDto的属性“Total”改为“Totals”,就会发现得到的“Totals”为0。理解了AutoMapper的解析方式,我们就要注意在编写变量、属性或是方法名称的时候一定要规范,这也是一种好的习惯。

Projection-简单到复杂

  Projection 翻译为投影,Flattening是由复杂结构简化,Projection正好相反,投影可以理解为由原始结构千变万化,我们看下两种转换结构:

 1     public class CalendarEvent
 2     {
 3         public DateTime EventDate { get; set; }
 4         public string Title { get; set; }
 5     }
 6
 7     public class CalendarEventForm
 8     {
 9         public DateTime EventDate { get; set; }
10         public int EventHour { get; set; }
11         public int EventMinute { get; set; }
12         public string Title { get; set; }
13     }

  CalendarEvent是原始结构,CalendarEventForm是我们需要转换后的结构,可以看到CalendarEventForm要比CalendarEvent结构复杂些,看下AutoMapper配置转换代码:

 1         public void Example()
 2         {
 3             var calendarEvent = new CalendarEvent
 4             {
 5                 EventDate = new DateTime(2008, 12, 15, 20, 30, 0),
 6                 Title = "Company Holiday Party"
 7             };
 8
 9             // 配置 AutoMapper
10             Mapper.CreateMap<CalendarEvent, CalendarEventForm>()
11                 .ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.EventDate.Date))//定义映射规则
12                 .ForMember(dest => dest.EventHour, opt => opt.MapFrom(src => src.EventDate.Hour))//定义映射规则
13                 .ForMember(dest => dest.EventMinute, opt => opt.MapFrom(src => src.EventDate.Minute));//定义映射规则
14
15             // 执行 mapping
16             CalendarEventForm form = Mapper.Map<CalendarEvent, CalendarEventForm>(calendarEvent);
17
18             Console.WriteLine("EventDate:"+form.EventDate);
19             Console.WriteLine("EventHour:" + form.EventHour);
20             Console.WriteLine("EventMinute:" + form.EventMinute);
21             Console.WriteLine("Title:" + form.Title);
22         }

  和Flattening不同的是,我们除了定义类型映射,还要自定义映射规则,src.EventDate.Date指向dest.EventDate,src.EventDate.Minute指向dest.EventMinute,src.EventDate.Hour指向dest.EventHour,当然我们还可以在MapFrom方法中做一些复杂的映射关系操作,MapFrom接受一个lambda表达式作为参数,可以是任何的Func表达式。Projection适用于由简单到复杂的结构映射,一般体现在业务场景很复杂的情况下。

  【更正:Projection也不一定适用在由简单到复杂的场景,应该说使用Projection就是把AutoMapper的映射配置交给用户来操作】

Configuration Validation-配置验证

  我们在使用Flattening的前提是我们需要转换的结构命名是没有错误的,但是如果我们没有使用PascalCase命名法,或者说我们命名是错误的,该怎么办呢?比如下面代码:

1         public class Source
2         {
3             public int SomeValue { get; set; }
4         }
5
6         public class Destination
7         {
8             public int SomeValuefff { get; set; }
9         }

  可以看到Source和Destination中的字段并不相对应,我们测试下AutoMapper映射:

  AssertConfigurationIsValid方法是验证结构映射的,如果配置不正确,会报“AutoMapperConfigurationException”异常错误,如何解决这个问题?你可能会说,就不能改下SomeValuefff的名称吗?这种方法可以,但是如果业务场景中必须要使用怎么办呢,看了上面Projection的映射配置,你可能想到解决方法了,如下:

1             Mapper.CreateMap<Source, Destination>()
2                 .ForMember(dest => dest.SomeValuefff, opt => opt.MapFrom(src => src.SomeValue));

  名称不对,我们可以自定义映射规则,虽然这种方式可以,但是如果业务场景中SomeValuefff并不需要,那我们改怎么办?既然有问题,就有解决之道,AutoMapper提供了Ignore方法,忽略不需要映射的数据结构,我们这样配置就可以了:

1             Mapper.CreateMap<Source, Destination>()
2                 .ForMember(dest => dest.SomeValuefff, opt => opt.Ignore());

Lists and Array-集合和数组

  有时候我们除了类型映射之外,还需要对集合类型进行映射,先看个示例:

 1             public void Example()
 2             {
 3                 var sources = new[]
 4                     {
 5                         new Source {Value = 5},
 6                         new Source {Value = 6},
 7                         new Source {Value = 7}
 8                     };
 9                 //配置AutoMapper
10                 Mapper.Initialize(cfg =>
11                 {
12                     cfg.CreateMap<Source, Destination>();
13                 });
14                 //配置和执行映射
15                 IEnumerable<Destination> ienumerableDest = Mapper.Map<Source[], IEnumerable<Destination>>(sources);
16                 ICollection<Destination> icollectionDest = Mapper.Map<Source[], ICollection<Destination>>(sources);
17                 IList<Destination> ilistDest = Mapper.Map<Source[], IList<Destination>>(sources);
18                 List<Destination> listDest = Mapper.Map<Source[], List<Destination>>(sources);
19
20                 Console.WriteLine("ienumerableDest.Count:" + ienumerableDest.Count());
21                 Console.WriteLine("icollectionDest.Count:" + icollectionDest.Count());
22                 Console.WriteLine("ilistDest.Count:" + ilistDest.Count());
23                 Console.WriteLine("listDest.Count:" + listDest.Count());
24             }

  转换结果:

  Source和Destination结构类型只有一个Value属性,可以看到对集合类型映射也很简单,只需要执行Mapper.Map泛型方法,指定需要转换的集合类型即可,AutoMapper所支持的集合类型包括:

  • IEnumerable
  • IEnumerable<T>
  • ICollection
  • ICollection<T>
  • IList
  • IList<T>
  • List<T>
  • Arrays

  我们在使用Mapper.Map执行类型映射的时候,如果来源类型支持上述集合类型,我们可以把来源类型省略掉,因为AutoMapper会自动判断传入对象sources的类型,如下:

1                 IEnumerable<Destination> ienumerableDest = Mapper.Map<IEnumerable<Destination>>(sources);
2                 ICollection<Destination> icollectionDest = Mapper.Map<ICollection<Destination>>(sources);
3                 IList<Destination> ilistDest = Mapper.Map<IList<Destination>>(sources);
4                 List<Destination> listDest = Mapper.Map<List<Destination>>(sources);

  还有一种情况是,在使用集合类型类型的时候,类型之间存在继承关系,例如下面我们需要转换的类型:

 1             public class ParentSource
 2             {
 3                 public int Value1 { get; set; }
 4             }
 5             public class ChildSource : ParentSource
 6             {
 7                 public int Value2 { get; set; }
 8             }
 9             public class ParentDestination
10             {
11                 public int Value1 { get; set; }
12             }
13             public class ChildDestination : ParentDestination
14             {
15                 public int Value2 { get; set; }
16             }

  ChildSource继承ParentSource,ChildDestination继承ParentDestination,看下AutoMapper配置转换代码:

 1             public void Example()
 2             {
 3                 var sources = new[]
 4                     {
 5                         new ParentSource(),
 6                         new ChildSource(),
 7                         new ParentSource()
 8                     };
 9                 //配置AutoMapper
10                 Mapper.Initialize(cfg =>
11                 {
12                     cfg.CreateMap<ParentSource, ParentDestination>()
13                         .Include<ChildSource, ChildDestination>();
14                     cfg.CreateMap<ChildSource, ChildDestination>();
15                 });
16                 //配置和执行映射
17                 var destinations = Mapper.Map<ParentSource[], ParentDestination[]>(sources);
18                 Console.WriteLine("destinations[0] Type:" + destinations[0].GetType().ToString());
19                 Console.WriteLine("destinations[1] Type:" + destinations[1].GetType().ToString());
20                 Console.WriteLine("destinations[2] Type:" + destinations[2].GetType().ToString());
21             }

  转换结果:

  注意在Initialize初始化CreateMap进行类型映射配置的时候有个Include泛型方法,签名为:“Include this configuration in derived types' maps”,大致意思为包含派生类型中配置,ChildSource是ParentSource的派生类,ChildDestination是ParentDestination的派生类,cfg.CreateMap<ParentSource, ParentDestination>().Include<ChildSource, ChildDestination>(); 这段代码只是说明ParentSource和ChildSource之间存在的关系,我们如果把这段代码注释掉,就会报上面“AutoMapperMappingException”类型指定不正确的异常错误,如果我们把下面这段代码:“cfg.CreateMap<ChildSource, ChildDestination>();”注释掉,转换结果为:

  虽然没有报“AutoMapperMappingException”异常,但是可以看出AutoMapper并没有从ChildSource类型映射到ChildDestination类型,而是自动映射到基类型,上面那段映射代码只是说明派生类和基类之间存在的关系,如果派生类需要映射的话,是需要添加派生类的映射的。

Nested mappings-嵌套映射

  我们上面说的集中映射方式都是简单类型映射,就是类型中并不包含其他类型的映射,如何在嵌套类型中执行映射?请看下面示例:

 1             public class OuterSource
 2             {
 3                 public int Value { get; set; }
 4                 public InnerSource Inner { get; set; }
 5             }
 6             public class InnerSource
 7             {
 8                 public int OtherValue { get; set; }
 9             }
10             public class OuterDest
11             {
12                 public int Value { get; set; }
13                 public InnerDest Inner { get; set; }
14             }
15             public class InnerDest
16             {
17                 public int OtherValue { get; set; }
18             }

  OuterSource和OuterDest类型是我们需要映射的类型,可以看到OuterSource类型中嵌套了InnerSource类型,OuterDest类型中嵌套了InnerDest类型,AutoMapper类型映射配置代码:

 1             public void Example()
 2             {
 3                 var source = new OuterSource
 4                 {
 5                     Value = 5,
 6                     Inner = new InnerSource { OtherValue = 15 }
 7                 };
 8                 //配置AutoMapper
 9                 Mapper.CreateMap<OuterSource, OuterDest>();
10                 Mapper.CreateMap<InnerSource, InnerDest>();
11                 //验证类型映射是否正确
12                 Mapper.AssertConfigurationIsValid();
13                 //执行映射
14                 var dest = Mapper.Map<OuterSource, OuterDest>(source);
15                 Console.WriteLine("dest.Value:" + dest.Value);
16                 Console.WriteLine("dest.Inner is null:" + (dest.Inner == null ? "true" : "false"));
17                 Console.WriteLine("dest.Inner.OtherValue:" + dest.Inner.OtherValue);
18             }

  转换结果:

  上面代码中可以看出,对于嵌套映射,我们不需要配置什么,只要指定下类型映射关系和嵌套类型映射关系就可以了,也就是这段代码:“Mapper.CreateMap<InnerSource, InnerDest>();” 其实我们在验证类型映射的时候加上Mapper.AssertConfigurationIsValid(); 这段代码看是不是抛出“AutoMapperMappingException”异常来判断类型映射是否正确,因为AssertConfigurationIsValid方法没有返回值,只能在catch中捕获了,个人感觉AutoMapper可以提供个bool类型的返回值,验证成功则返回true。

后记

  示例代码下载:http://pan.baidu.com/s/10A7WM

  贪多嚼不烂,关于AutoMapper的使用先整理这些,后面会陆续更新,还请关注。

  AutoMapper在配置类型映射最注意的一点是,类型中的名称一定要按照PascalCase命名规则(Projection和Ignore除外)。

  如果你觉得本篇文章对你有所帮助,请点击右下部“推荐”,^_^

  参考资料:

  • https://github.com/AutoMapper/AutoMapper/wiki
  • http://www.cnblogs.com/dudu/archive/2011/12/16/2284828.html
  • http://www.cnblogs.com/ego/archive/2009/05/13/1456363.html
  • http://www.cnblogs.com/jiguixin/archive/2011/09/19/2181521.html
  • http://blog.csdn.net/yujunwu2525/article/details/7850486
posted on 2019-04-01 11:02 NET未来之路 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/lonelyxmas/p/10635192.html

【AutoMapper官方文档】DTO与Domin Model相互转换(上)相关推荐

  1. 【AutoMapper官方文档】DTO与Domin Model相互转换(中)

    写在前面 AutoMapper目录: [AutoMapper官方文档]DTO与Domin Model相互转换(上) [AutoMapper官方文档]DTO与Domin Model相互转换(中) [Au ...

  2. Hyperledger Fabric 2.0 官方文档中文版 第6章 教程(上)

    Hyperledger Fabric 2.0 官方文档中文版第6章 教程上 总目录 6.教程(上) 将智能合约部署到通道 启动网络 Logspout设置 打包智能合约 安装链码包 批准链码定义 将链码 ...

  3. JMeter官方文档阅读及实践笔记(上)

    JMeter笔记 一.测试计划元件概览 本节简单介绍测试计划的不同部分. 最小测试将包括测试计划.线程组和一个或多个采样器. 1.Thread Group,线程组 线程组元素是任何测试计划的起点.所有 ...

  4. 【AutoMapper官方文档】DTO与Domin Model相互转换(下)

    Mapping Inheritance-映射继承 关于映射继承,其实在"Lists and Array-集合和数组"这一节点有提到,但是只是说明下AutoMapper解决映射继承所 ...

  5. DTO与Domin Model相互转换(上)

    Flattening-复杂到简单 Flattening 翻译为压扁.拉平.扁平化的意思,可以理解为使原有复杂的结构变得简化,我们先看下领域模型和DTO代码: 1 public class Order ...

  6. pywin32官方说明文档_为什么你应该看官方文档而不是搜索博客文章

    摄影:产品经理寿喜锅 很多人在学习一门新技术的时候,不去看官方文档,而是喜欢直接在网上搜索别人的教程.如果运气不好搜索到了CSDN上面的装逼货辣鸡文章,那么你的学习生涯可能就此终止.即使搜索到写得很详 ...

  7. 喜大普奔!Django官方文档终于出中文版了

    喜大普奔!Django官方文档终于出中文版了 文章来源:企鹅号 - Crossin的编程教室 昨天经 Sur 同学告知才发现,Django 官方文档居然支持中文了! 之所以让我觉得惊喜与意外,是因为: ...

  8. Mybatis官方文档及使用简记

    Mybatis官方文档及使用简记 数据库建表 入门案例 无mapper类最传统的用法 使用mybatis generator 使用mybatis-generator mybatis-spring整合 ...

  9. react router官方文档_阿里开源可插拔 React 跨端框架 UmiJS

    点击上方"开发者技术前线",选择"星标" 18:30 在看 真爱 作者:Tamic  |  编辑: 可可 阿里之前开源:阿里闲鱼开源 Flutter 应用框架 ...

最新文章

  1. JNI开发之锅炉压力监控器
  2. MySQL的库表详细操作
  3. python pcm,python pcm音频添加头转成Wav格式文件的方法
  4. 中的挂起是什么意思_仪表板亮奇怪指示灯,乌龟晒太阳是什么意思?老司机:不懂别上路...
  5. servlet ---- 案例(简单)优化
  6. ios与java交互_5、与iOS、Android的交互 实践篇——主动调用
  7. java 反射 动态_java实现反射,动态配置
  8. 32. Magento log()方法
  9. 安卓模拟器所有版本系统xp框架安装方法(万能)
  10. android开启vr模式,三星Gear VR开发者模式怎么打开
  11. 眼睛容易干燥疲劳怎么办?
  12. FFmpeg无损转换ts为mp4
  13. 从一杯果汁浅谈点点医生充值提现模块设计
  14. win10家庭版 mysql_MySQL下载安装详解(win10家庭版)
  15. 优秀课程案例:使用Scratch制作一个射击类游戏-360度旋转射击!
  16. cad绘制正八边形_什么是CAD?为什么要学CAD?该怎么学CAD?
  17. AI绘图–Disco Diffusion使用指南+本地化保姆级教程
  18. 日野汽车数据作假召开记者招待会
  19. 用Mapviz显示卫星地图轨迹
  20. 弘辽科技:淘宝订单编号会透露个人信息吗?淘宝如何查看订单编号

热门文章

  1. VIO-SLAM中的欧拉积分、中点积分与龙格-库塔积分
  2. 虚幻争霸服务器文件,《虚幻争霸》公开最后一轮免费资源
  3. Caffe代码导读(3):LevelDB例程
  4. 温度传感器的标准介绍与安装
  5. ftp上传文件报错“200 Type set tol.200 PORT command scesful.4MUM np for he Uicde hater eit inte onetmult”
  6. 为啥要用凸透镜做相机的镜头呢 ?
  7. 如何学习3D Three.js 3D引擎
  8. 进程相关概念 读取当前进程getpid 读取父进程id getppid
  9. 《淘宝店铺 大数据营销+SEO+爆款打造 一册通》一一2.1 生意参谋平台概述
  10. AIE红色/蓝色/绿色荧光聚苯乙烯微球/比色-荧光双信号ALE荧光微球的相关研究