DTO学习系列之AutoMapper(六)----EntityFramework和AutoMapper的婚后生活
写在前面
- 我到底是什么?
- 越界的可怕
- 做好自己
- 后记
文章标题主要关键字:mapping DTOs to Entities,注意并不是“Entities to DTOs”,表示实体对象到DTO的转换,并做一些CURD操作,代码看似合理,但是好像又有些不合理,为什么?主要是对所涉及的概念不清晰,EntityFramework、AutoMapper和DTO,他们到底是什么?有没有在一起的可能?请接着往下看。
我到底是什么?
EntityFramework(父亲)、AutoMapper(母亲)和DTO(孩子)你我都知道的官方定义:
- EntityFramework:是微软以 ADO.NET 为基础所发展出来的对象关系对应 (O/R Mapping) 解决方案。
- AutoMapper:Object-Object Mapping工具。
- DTO:数据传输对象(Data Transfer Object)。
EntityFramework的定义中有“ORM“关键字,那ORM又是什么?
ORM:对象关系映射 (Object/Relation Mapping),是随着面向对象的软件开发方法发展而产生的。面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应用环 境中永久存放数据的主流数据存储系统。对象和关系数据是业务实体的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之 间存在关联和继承关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系。 --百度百科
概念清楚了就好办了,我们再来分析下,从上面定义可以看出:AutoMapper是Object-Object映射工具,EntityFramework是一种ORM框架,DTO是数据传输对象(Data Transfer Object),请注意这三个定义中都包含对象(Object)关键字,毫无疑问,AutoMapper所做的工作就是ORM中的“O”和DTO中的“O”之间的映射转换。
DTO中的“O”比较好理解,就是数据传输对象,不包含任何的业务逻辑,只是存储数据的一种对象,用于各层之间的数据传递。一般的项目都会采用分层设计(也就是常见的三层架构),每一层都是一个相对内聚的设计,一种松耦合结构。而层与层之间进行通讯的就是DTO,而这个“O”常常不是ORM的O。其实也可能不是DomainEntity,也不是ViewModel,但是它却有可能通过组合、分解等方式进行转换。
那ORM中“O”是什么意思?关于ORM的使用,网上有很多的争论,抛开性能 问题,有人提出说“ORM注定了业务逻辑和数据库的高度耦合”,我觉得这种理解是错误的。ORM的“O”是数据对象,与表等有一定的偶合,但它从架构设计 上来说,只是仓储层的内聚设计,与业务逻辑无关(当然现在很多小系统会用它来直接替代业务逻辑对象),而真正的业务逻辑对象(按领域驱动设计来说)是领域 对象,真正的的系统核心对象是领域对象,而数据对象是可变的,领域对象则相对稳定,数据对象到领域对象是通过仓储层的适配器来实现的。
从上面的结论中可以看出,ORM中的“O”是数据对象。关于数据对象,有人说 数据对象是稳定的,那要看数据是基于“数据”的设计,还是基于“对象”的设计。如果是基于“对象”的设计,那么设计之初,就必须把业务对象分析清楚、我们 常把它说成领域对象,其实这个对象基本是稳定的(至少核心部分是稳定,如果核心对象变了,那是另一个话题,需求变了),而数据对象可能就不一定了,有可能 SqlServer数据有的类型其它数据库没有,同样随着重构的进行,为了提高性能,可能会对一些表进行拆分和组合,原先在一个表中的数据,分成了两个 表,或在视图中了。但不管数据表怎么变化,最终也只是仓储层内部的实现方式变了,当然ORM的“O”也会变,但出了这一层,一切都还是原来的样子。
理解这些概念很重要,理解了你会发现,我为什么把 EntityFramework看做“父亲”,AutoMapper看做“母亲”,DTO看做“孩子”,当然这只是某种意义上的关系比作,只有在这三者结 合才会出现,比如DTO可以脱离EntityFramework和AutoMapper独立存在,但他就不是“孩子”的概念了。
越界的可怕
什么叫越界?就是不是你干的事你却干了,所做的工作超出自己的范围之外,Roger Alsing的“Horrible”文章我觉得就是在表达这个意思。AutoMapper的工作范围只是对象之间的映射转换,也就是说EntityFramework中的“数据对象”到“DTO”之间的映射转换,但如果涉及到一些数据访问或是操作,这就是AutoMapper的越界行为,因为这些操作并不在她的职责范围之内,而应该是EntityFramework所做的工作。
某种意义上,可以看做:AutoMapper(母亲)只是EntityFramework(父亲)和DTO(孩子)之间的桥梁或是沟通,至于赚钱养家的事就交给EntityFramework(父亲)去做,如果AutoMapper(母亲)帮助EntityFramework(父亲)去赚钱养家,可能会造成相反的效果,也就是说AutoMapper(母亲)请做好“全职太太”即可。
我们来看下AutoMapper(母亲)的“越界行为”:
1 public void CreateOrUpdateOrder(OrderDTO orderDTO) 2 { 3 var config = new ... 4 5 //create an instanced mapper instead of the static API 6 var mapper = new AutoMapper.MapperEngine(config); 7 var context = ... //get some DbContext 8 9 config.CreateMap<OrderDTO,Order>() 10 .ConstructUsing((OrderDTO orderDTO) => 11 { 12 if (orderDTO.Id == 0) 13 { 14 var order = new Order(); 15 context.OrderSet.Add(order); 16 return order; 17 } 18 return context.OrderSet.Include("Details").First(o => o.Id == orderDTO.Id); 19 }) 20 //find out what details no longer exist in the DTO and delete the corresponding entities 21 //from the dbcontext 22 .BeforeMap((dto, o) => 23 { 24 o 25 .Details 26 .Where(d => !dto.Details.Any(ddto => ddto.Id == d.Id)).ToList() 27 .ForEach(deleted => context.DetailSet.Remove(deleted)); 28 }); 29 30 config.CreateMap<DetailDTO, Detail>() 31 .ConstructUsing((DetailDTO detailDTO) => 32 { 33 if (detailDTO.Id == 0) 34 { 35 var detail = new Detail(); 36 context.DetailSet.Add(detail); 37 return detail; 38 } 39 return context.DetailSet.First(d => d.Id == detailDTO.Id); 40 }); 41 42 mapper.Map<OrderDTO,Order>(orderDTO); 43 44 context.SaveChanges(); 45 }
AutoMapper的ConstructUsing的用法,ConstructUsing表示自定义类型转换器,发生在映射之前,对映射的操作做一些处理并返回相应的目标类型,注意这里的处理并不是EntityFramework中的持久化,如果在AutoMapper的自定义类型转换器中做这些操作,就显得有点不伦不类了。
关于AutoMapper这样的“越界行为”,Roger Alsing总结出了其中的优缺点,本人就不翻译了,以免起到误读的效果。
优点:
- Looks simple on paper
- Easy to implement on read side and client side
缺点:
- Bloody horrible to implement on the write side, and gets even worse the larger the DTO is
- Relies on magic names if using AutoMapper
- Network ineffective if dealing with large DTOs
- You lose semantics and intentions, there is no way to know WHY a change happened
做好自己
如果我们在上面代码中去掉AutoMapper,将会变得如何?请看下面:
1 public void CreateOrUpdateOrder(OrderDTO orderDTO) 2 { 3 var ctx = ... //get some DbContext 4 var order = ctx.OrderSet.FirstOrDefault(o => o.Id == orderDTO.Id); 5 if (order == null) 6 { 7 order = new Order(); 8 ctx.OrderSet.Add(order); 9 } 10 11 //Map properties 12 order.Address = orderDTO.Address; 13 14 //remove deleted details 15 order.Details 16 .Where(d => !orderDTO.Details.Any(detailDTO => detailDTO.Id == d.Id)) 17 .Each(deleted => ctx.DetailSet.Remove(deleted)); 18 19 //update or add details 20 orderDTO.Details.Each(detailDTO => 21 { 22 var detail = order.Details.FirstOrDefault(d => d.Id == detailDTO.Id); 23 if (detail == null) 24 { 25 detail = new Detail(); 26 order.Details.Add(detail); 27 } 28 detail.Name = detailDTO.Name; 29 detail.Quantity = detailDTO.Quantity; 30 }); 31 32 context.SaveChanges(); 33 }
这样代码更加清洁,与使用AutoMapper的代码形成了明显的对比,但如果去掉AutoMapper也就失去了DTO的意义,试想没有母亲哪来的孩子?但是如果EntityFramework(父亲)、AutoMapper(母亲)和DTO(孩子)这三口之家想和谐的生活在一起,那怎么办?就是AutoMapper只要负责对象映射转换即可,也就是做EntityFramework(父亲)和DTO(孩子)之间的“沟通桥梁”,也就是“全职太太”:
1 Mapper.CreateMap<OrderDTO, Order>(); 2 Mapper.CreateMap<DetailDTO, Detail>(); 3 using (var context = new OrderContext()) 4 { 5 var existOrder = context.Orders.FirstOrDefault(order => order.Id == orderDTO.Id); 6 if (existOrder == null) 7 { 8 var order = Mapper.Map<OrderDTO, Order>(orderDTO); 9 context.Orders.Add(order); 10 context.Details.AddRange(order.Details); 11 context.SaveChanges(); 12 } 13 }
后记
示例代码下载:http://pan.baidu.com/s/1o6EzFq6
做了“全职太太”的AutoMapper,就这样和EntityFramework幸福的生活下去了,看到这有人又可能有疑问,上篇中AutoMapper为EntityFramework做的IQueryable扩展是不是“越界行为”,注意QueryableExtensions只是AutoMapper所做的扩展,并不是代替EntityFramework去完成持久化操作。
参考资料:
http://www.cnblogs.com/xishuai/p/3720662.html
DTO学习系列之AutoMapper(六)----EntityFramework和AutoMapper的婚后生活的更多相关文章
- DTO学习系列之AutoMapper(五)----当EntityFramework爱上AutoMapper
有时候相识即是一种缘分,相爱也不需要太多的理由,一个眼神足矣,当EntityFramework遇上AutoMapper,就是如此,恋爱虽易,相处不易. 在DDD(领域驱动设计)中,使用AutoMapp ...
- Dubbo学习系列之十六(ELK海量日志分析框架)
外卖公司如何匹配骑手和订单?淘宝如何进行商品推荐?或者读者兴趣匹配?还有海量数据存储搜索.实时日志分析.应用程序监控等场景,Elasticsearch或许可以提供一些思路,作为业界最具影响力的海量搜索 ...
- DTO学习系列之AutoMapper(四)
本篇目录: Mapping Inheritance-映射继承 Queryable Extensions (LINQ)-扩展查询表达式 Configuration-配置 Conditional Mapp ...
- DTO学习系列之AutoMapper(三)
本篇目录: Custom Type Converters-自定义类型转换器 Custom Value Resolvers-自定义值解析器 Null Substitution-空值替换 Containe ...
- DTO学习系列之AutoMapper(二)
本篇目录: Flattening-复杂到简单 Projection-简单到复杂 Configuration Validation-配置验证 Lists and Array-集合和数组 Nested m ...
- DTO学习系列之AutoMapper(一)
一.前言 DTO(Data Transfer Object)数据传输对象,注意关键字“数据”两个字,并不是对象传输对象(Object Transfer Object),所以只是传输数据,并不包含领域业 ...
- tkinter学习系列之(六)Radiobutton控件
目录 目录 前言 (一)基本属性 (二)在Frame里布局: 目录 前言 Radiobutton单选框,在一组选框中,只能选中一个. (一)基本属性 (1)特有属性: value 按钮的值 varia ...
- WP8.1学习系列(第二十六章)——控件模板
在本文中 自定义控件模板示例 指定控件的可视结构. 指定控件的可视行为 使用工具轻松处理主题 控件和辅助功能 了解有关控件默认模板的详细信息 控件模板中的主题资源 相关主题 在 XAML 框架中,如果 ...
- WP8.1学习系列(第十六章)——交互UX之命令模式
命令模式 在本文中 命令类型 命令放置 相关主题 你可以在应用商店应用的几个曲面中放置命令和控件,包括应用画布.弹出窗口.对话框和应用栏.在正确的时间选择合适的曲面可能就是易于使用的应用和很难使用 ...
随机推荐
- 东软实训2-在jsp中使用javaBean
在JSP中可以像使用普通类一样访问JavaBean,在脚本元素中实例化类的对象,调用对象的方法.JSP提供了3个动作元素,和来访问JavaBean. 1.1 动作用于初始化JavaBean,或者定位一 ...
- 【vc】5_文本编程
1.插入符(Caret): (1) 文本插入符 函数的原型声明:(CWnd类) void CreateSolidCaret ( int Nwidth, int nHeight ); ·nwidth:指 ...
- 移动端-jquery Ajax执行滑动到底部进行加载 指定加载次数
过完年了,又要有新的项目了,趁暴风雨前的宁静,就把之前让我挺头疼的ajax加载给总结了一下.在此分享. 声明下:笔者对于ajax也是新手,如有更好的写法或可以优化的写法,还望指正! 项目需求: 移动端 ...
- 使用PHP解压文件Unzip
这是一个非常方便的PHP函数从.zip文件解压缩文件.它有两个参数:第一个是压缩文件的路径和第二 function unzip_file($file, $destination) { // creat ...
- 使用PHP对数据库输入进行恶意代码清除
这是一个有用的PHP函数清理了所有的输入数据,并删除代码注入的几率. function sanitize_input_data($input_data) { $input_data = trim(ht ...
- Hadoop-2.7.1集群环境搭建
摘自:http://blog.csdn.net/u014039577/article/details/49813531 由于日志数据量越来越大,数据处理的逻辑越来越复杂,同时还涉及到大量日志需要批处理 ...
- new Handler()和new Handler(Looper.getMainLooper())的区别
一个帖子的整理: Handler一定要在主线程实例化吗?new Handler()和new Handler(Looper.getMainLooper())的区别如果你不带参数的实例化:Handler ...
- tomcat文件夹与文件解析
今天看到一篇不错的文章,如下: /bin:存放启动和关闭tomcat的脚本文件: /conf:存放tomcat的各种配置文件,比如:server.xml/ server/lib:存放tomcat服务器 ...
- windows mongodb 安装
window平台一下所有命令 务必以管理员身份运行 且在window系统命令行下,如git bash不可以 具体以管理员身份运行的快捷键是 1. win+x 2. shift + a 设置文件存储目录 ...
- Bloglines订阅Blog部落格RSS网摘 - Blog透视镜
网络信息蓬勃发展,Blog部落格越来越普及,如果逐一地去浏览网站,势必费时费力,倘若信息可以自己送上门,那就可以节省不少时间,就好像看报纸的标题,有兴趣才点连结,进到网站浏览文章内容,Blogline ...