【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
转载于:https://www.cnblogs.com/lonelyxmas/p/10635192.html
【AutoMapper官方文档】DTO与Domin Model相互转换(上)相关推荐
- 【AutoMapper官方文档】DTO与Domin Model相互转换(中)
写在前面 AutoMapper目录: [AutoMapper官方文档]DTO与Domin Model相互转换(上) [AutoMapper官方文档]DTO与Domin Model相互转换(中) [Au ...
- Hyperledger Fabric 2.0 官方文档中文版 第6章 教程(上)
Hyperledger Fabric 2.0 官方文档中文版第6章 教程上 总目录 6.教程(上) 将智能合约部署到通道 启动网络 Logspout设置 打包智能合约 安装链码包 批准链码定义 将链码 ...
- JMeter官方文档阅读及实践笔记(上)
JMeter笔记 一.测试计划元件概览 本节简单介绍测试计划的不同部分. 最小测试将包括测试计划.线程组和一个或多个采样器. 1.Thread Group,线程组 线程组元素是任何测试计划的起点.所有 ...
- 【AutoMapper官方文档】DTO与Domin Model相互转换(下)
Mapping Inheritance-映射继承 关于映射继承,其实在"Lists and Array-集合和数组"这一节点有提到,但是只是说明下AutoMapper解决映射继承所 ...
- DTO与Domin Model相互转换(上)
Flattening-复杂到简单 Flattening 翻译为压扁.拉平.扁平化的意思,可以理解为使原有复杂的结构变得简化,我们先看下领域模型和DTO代码: 1 public class Order ...
- pywin32官方说明文档_为什么你应该看官方文档而不是搜索博客文章
摄影:产品经理寿喜锅 很多人在学习一门新技术的时候,不去看官方文档,而是喜欢直接在网上搜索别人的教程.如果运气不好搜索到了CSDN上面的装逼货辣鸡文章,那么你的学习生涯可能就此终止.即使搜索到写得很详 ...
- 喜大普奔!Django官方文档终于出中文版了
喜大普奔!Django官方文档终于出中文版了 文章来源:企鹅号 - Crossin的编程教室 昨天经 Sur 同学告知才发现,Django 官方文档居然支持中文了! 之所以让我觉得惊喜与意外,是因为: ...
- Mybatis官方文档及使用简记
Mybatis官方文档及使用简记 数据库建表 入门案例 无mapper类最传统的用法 使用mybatis generator 使用mybatis-generator mybatis-spring整合 ...
- react router官方文档_阿里开源可插拔 React 跨端框架 UmiJS
点击上方"开发者技术前线",选择"星标" 18:30 在看 真爱 作者:Tamic | 编辑: 可可 阿里之前开源:阿里闲鱼开源 Flutter 应用框架 ...
最新文章
- JNI开发之锅炉压力监控器
- MySQL的库表详细操作
- python pcm,python pcm音频添加头转成Wav格式文件的方法
- 中的挂起是什么意思_仪表板亮奇怪指示灯,乌龟晒太阳是什么意思?老司机:不懂别上路...
- servlet ---- 案例(简单)优化
- ios与java交互_5、与iOS、Android的交互 实践篇——主动调用
- java 反射 动态_java实现反射,动态配置
- 32. Magento log()方法
- 安卓模拟器所有版本系统xp框架安装方法(万能)
- android开启vr模式,三星Gear VR开发者模式怎么打开
- 眼睛容易干燥疲劳怎么办?
- FFmpeg无损转换ts为mp4
- 从一杯果汁浅谈点点医生充值提现模块设计
- win10家庭版 mysql_MySQL下载安装详解(win10家庭版)
- 优秀课程案例:使用Scratch制作一个射击类游戏-360度旋转射击!
- cad绘制正八边形_什么是CAD?为什么要学CAD?该怎么学CAD?
- AI绘图–Disco Diffusion使用指南+本地化保姆级教程
- 日野汽车数据作假召开记者招待会
- 用Mapviz显示卫星地图轨迹
- 弘辽科技:淘宝订单编号会透露个人信息吗?淘宝如何查看订单编号
热门文章
- VIO-SLAM中的欧拉积分、中点积分与龙格-库塔积分
- 虚幻争霸服务器文件,《虚幻争霸》公开最后一轮免费资源
- Caffe代码导读(3):LevelDB例程
- 温度传感器的标准介绍与安装
- ftp上传文件报错“200 Type set tol.200 PORT command scesful.4MUM np for he Uicde hater eit inte onetmult”
- 为啥要用凸透镜做相机的镜头呢 ?
- 如何学习3D Three.js 3D引擎
- 进程相关概念 读取当前进程getpid 读取父进程id getppid
- 《淘宝店铺 大数据营销+SEO+爆款打造 一册通》一一2.1 生意参谋平台概述
- AIE红色/蓝色/绿色荧光聚苯乙烯微球/比色-荧光双信号ALE荧光微球的相关研究