写在前面

  • 我到底是什么?
  • 越界的可怕
  • 做好自己
  • 后记

  文章标题主要关键字: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的婚后生活的更多相关文章

  1. DTO学习系列之AutoMapper(五)----当EntityFramework爱上AutoMapper

    有时候相识即是一种缘分,相爱也不需要太多的理由,一个眼神足矣,当EntityFramework遇上AutoMapper,就是如此,恋爱虽易,相处不易. 在DDD(领域驱动设计)中,使用AutoMapp ...

  2. Dubbo学习系列之十六(ELK海量日志分析框架)

    外卖公司如何匹配骑手和订单?淘宝如何进行商品推荐?或者读者兴趣匹配?还有海量数据存储搜索.实时日志分析.应用程序监控等场景,Elasticsearch或许可以提供一些思路,作为业界最具影响力的海量搜索 ...

  3. DTO学习系列之AutoMapper(四)

    本篇目录: Mapping Inheritance-映射继承 Queryable Extensions (LINQ)-扩展查询表达式 Configuration-配置 Conditional Mapp ...

  4. DTO学习系列之AutoMapper(三)

    本篇目录: Custom Type Converters-自定义类型转换器 Custom Value Resolvers-自定义值解析器 Null Substitution-空值替换 Containe ...

  5. DTO学习系列之AutoMapper(二)

    本篇目录: Flattening-复杂到简单 Projection-简单到复杂 Configuration Validation-配置验证 Lists and Array-集合和数组 Nested m ...

  6. DTO学习系列之AutoMapper(一)

    一.前言 DTO(Data Transfer Object)数据传输对象,注意关键字“数据”两个字,并不是对象传输对象(Object Transfer Object),所以只是传输数据,并不包含领域业 ...

  7. tkinter学习系列之(六)Radiobutton控件

    目录 目录 前言 (一)基本属性 (二)在Frame里布局: 目录 前言 Radiobutton单选框,在一组选框中,只能选中一个. (一)基本属性 (1)特有属性: value 按钮的值 varia ...

  8. WP8.1学习系列(第二十六章)——控件模板

    在本文中 自定义控件模板示例 指定控件的可视结构. 指定控件的可视行为 使用工具轻松处理主题 控件和辅助功能 了解有关控件默认模板的详细信息 控件模板中的主题资源 相关主题 在 XAML 框架中,如果 ...

  9. WP8.1学习系列(第十六章)——交互UX之命令模式

    命令模式   在本文中 命令类型 命令放置 相关主题 你可以在应用商店应用的几个曲面中放置命令和控件,包括应用画布.弹出窗口.对话框和应用栏.在正确的时间选择合适的曲面可能就是易于使用的应用和很难使用 ...

随机推荐

  1. POJ 3045 Cow Acrobats (贪心)

    POJ 3045 Cow Acrobats 这是个贪心的题目,和网上的很多题解略有不同,我的贪心是从最下层开始,每次找到能使该层的牛的风险最小的方案, 记录风险值,上移一层,继续贪心. 最后从遍历每一 ...

  2. 贪心 CF 332 C 好题 赞

    题目链接: http://codeforces.com/problemset/problem/332/C 题目意思: 有n个命令,要通过p个,某主席要在通过的p个中选择k个接受. 每个任务有两个值ai ...

  3. hibernate级联保存,更新个人遇到的问题

    在级联更新的时候,数据库中的数据是增加的,只是外键不存在,导致这样的问题产生的原因是,字表主键ID没有添加到集合中,导致Hibernate找不到子项而执行更新.

  4. Go语言之异常处理

    在编写Go语言代码的时候,我们应该习惯使用error类型值来表明非正常的状态.作为惯用法,在Go语言标准库代码包中的很多函数和方法也会以返回error类型值来表明错误状态及其详细信息. error是一 ...

  5. [Mugeda HTML5技术教程之5] 创建新作品

    前一节,我们介绍了Mugeda Studio.这一节我们讲一下怎么通过Studio创建新作品.首先登陆网站,如果还没有登陆账号,你可以登录 www.mugeda.com 免费注册一个.登录网站后,点击 ...

  6. iOS学习之自定义UItableViewCell

    在项目开发中,大部分情况下我们都需要自定义UITableViewCell, 今天就重点整理一下目前自己已经学过的自定义Cell的一些注意事项; 分步骤来写吧: 1.将自定义的Cell定义为属性; 2. ...

  7. vmware workstation 10.0.1 install mac os mavericks

    由于项目当中有ios app开发,第一感觉就是mac开发环境会比较适合开发与调试(虽然linux.windows都有相应的oc开发环境). 由于我是用windows7搞.net开发的,所以其他系统接触 ...

  8. USART笔记 基于STM32F107VCT6

    USART   通用同步异步收发器 通用同步异步收发器(USART)提供了一种灵活的方法与使用工业标准NRZ异步串行数据格式的外部设备之间进行全双工数据交换.USART利用分数波特率发生器提供宽范围的 ...

  9. MySQL常用Json函数

    官方文档:JSON Functions Name Description JSON_APPEND() Append data to JSON document JSON_ARRAY() Create ...

  10. df 和du 命令统计磁盘空间不准确

    Linux & Unix 中 df 和 du 命令统计磁盘空间数值不一致 经常会使用 df 和 du 分别查看磁盘空闲空间和占用空间,偶尔会发现 df(空闲空间) 会小于磁盘大小减去 du(占 ...