Entity Framework 实体框架的形成之旅--数据传输模型DTO和实体模型Entity的分离与联合
在使用Entity Framework 实体框架的时候,我们大多数时候操作的都是实体模型Entity,这个和数据库操作上下文结合,可以利用LINQ等各种方便手段,实现起来非常方便,一切看起来很美好。但是如果考虑使用WCF的时候,可能就会碰到很多相关的陷阱或者错误了。因为实体模型Entity的对象可能包括了其他实体的引用,在WCF里面就无法进行序列化,出现错误;而且基于WCF的时候,可能无法有效利用Express表达式,无法直接使用LINQ等问题都一股脑出现了。本文基于上面的种种问题,阐述了我的整个Entity Framework 实体框架的解决思路,并且在其中引入了数据传输模型DTO来解决问题,本文主要介绍数据传输模型DTO和实体模型Entity的分离与联合,从而实现我们通畅、高效的WCF应用框架。
1、实体模型Entity无法在WCF中序列化
例如,我们定义的Entity Framework 实体类里面包含了其他对象的引用,例如有一个Role对象,有和其他表的关联关系的,默认使用传统方式,在实体类里面添加[DataContract]方式。
/// <summary>
/// 角色
/// </summary>
[DataContract(IsReference = true)]
public class Role
{
/// <summary>
/// 默认构造函数(需要初始化属性的在此处理)
/// </summary>
public Role()
{
this.ID= System.Guid.NewGuid().ToString(); //Children = new HashSet<Role>();
//Users = new HashSet<User>();
} #region Property Members [DataMember]
public virtual string ID { get; set; } /// <summary>
/// 角色名称
/// </summary>
[DataMember]
public virtual string Name { get; set; } /// <summary>
/// 父ID
/// </summary>
[DataMember]
public virtual string ParentID { get; set; } [DataMember]
public virtual ICollection<Role> Children { get; set; } [DataMember]
public virtual Role Parent { get; set; } [DataMember]
public virtual ICollection<User> Users { get; set; } #endregion }
在WCF服务接口里面使用代码如下所示。
public class Service1 : IService1
{
public List<Role> GetAllRoles()
{
return IFactory.Instance<IRoleBLL>().GetAll().ToList();
} .........
那么我们在WCF里面使用的时候,会得到下面的提示。
接收对 http://localhost:11229/Service1.svc 的 HTTP 响应时发生错误。这可能是由于服务终结点绑定未使用 HTTP 协议造成的。这还可能是由于服务器中止了 HTTP 请求上下文(可能由于服务关闭)所致。有关详细信息,请参见服务器日志。
默认情况下,Entity Framework为了支持它的一些高级特性(延迟加载等),默认将自动生成代理类是设置为true。如果我们需要禁止自动生成代理类,那么可以在数据库操作上下文DbContext里面进行处理设置。
Configuration.ProxyCreationEnabled = false;
如果设置为false,那么WCF服务可以工作正常,但是实体类对象里面的其他对象集合则为空了,也就是WCF无法返回这些引用的内容。
同时,在Entity Framework框架里面,这种把实体类贯穿各个层里面,也是一种不推荐的做法,由于WCF里面传输的数据都是序列号过的数据,也无法像本地一样利用LINQ来实现数据的处理操作的。
那么我们应该如何构建基于WCF引用的Entity Framework实体框架呢?
2、数据传输对象DTO的引入
前面介绍了直接利用Entity Framework实体类对象的弊端,并且如果是一路到底都使用这个实体类,里面的很多对象引用都是空的,对我们在界面层使用不便,而且也可能引发了很多WCF框架里面的一些相关问题。
我们根据上面的问题,引入了一个DTO(数据传输对象)的东西。
数据传输对象(DTO)是没有行为的POCO对象,它的目的只是为了对领域对象进行数据封装,实现层与层之间的数据传递,界面表现层与应用层之间是通过数据传输对象(DTO)进行交互的。数据传输对象DTO本身并不是业务对象,数据传输对象是根据UI的需求进行设计的。
这个对象和具体数据存储的实体类是独立的,它可以说是实体类的一个映射体,名称可以和实体类不同,属性数量也可以实体类不一致。那么既然在实体对象层外引入了另外一个DTO对象层,那么相互转换肯定是避免不了的了,我们为了避免手工的映射方式,引入了另外一个强大的自动化映射的工具AutoMapper,来帮助我们快速、高效、智能的实现两个层对象的映射处理。

AutoMapper的使用比较简单,一般如果对象属性一直,他们会实现属性自动映射了,如下所示。
Mapper.CreateMap<RoleInfo, Role>();
如果两者的属性名称不一致,那么可以通过ForMember方式指定,类似下面代码所示。
AutoMapper.Mapper.CreateMap<BlogEntry, BlogPostDto>()
.ForMember(dto => dto.PostId, opt => opt.MapFrom(entity => entity.ID));
AutoMapper也可以把映射信息写到一个类里面,然后统一进行加载。
Mapper.Initialize(cfg => {
cfg.AddProfile<OrganizationProfile>();
});
那么基于上面的图示模式,由于我们采用代码生成工具自动生成的DTO和Entity,他们属性名称是保持一致的,那么我们只需要在应用层对它们两者对象进行相互映射就可以了。
public class RoleService : BaseLocalService<RoleInfo, Role>, IRoleService
{
private IRoleBLL bll = null; public RoleService() : base(IFactory.Instance<IRoleBLL>())
{
bll = baseBLL as IRoleBLL; //DTO和Entity模型的相互映射
Mapper.CreateMap<RoleInfo, Role>();
Mapper.CreateMap<Role, RoleInfo>();
}
}
基于这个内部对接的映射关系,我们就可以在Facade接口层提供统一的DTO对象服务,而业务逻辑层(也就是利用Entity Framework 实体框架的处理成)则依旧使用它的Entity对象来传递。下面我提供几个封装好的基类接口供了解DTO和Entity的相互衔接处理。
1)传入DTO对象,并转换为Entity对象,使用EF对象插入。
/// <summary>
/// 插入指定对象到数据库中
/// </summary>
/// <param name="dto">指定的对象</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c></returns>
public virtual bool Insert(DTO dto)
{
Entity t = dto.MapTo<Entity>();
return baseBLL.Insert(t);
}
2)根据条件从EF框架中获取Entity对象,并转换后返回DTO对象
/// <summary>
/// 查询数据库,返回指定ID的对象
/// </summary>
/// <param name="id">ID主键的值</param>
/// <returns>存在则返回指定的对象,否则返回Null</returns>
public virtual DTO FindByID(object id)
{
Entity t = baseBLL.FindByID(id);
return t.MapTo<DTO>();
}
3)根据条件从EF框架中获取Entity集合对象,并转换为DTO列表对象
/// <summary>
/// 返回数据库所有的对象集合
/// </summary>
/// <returns></returns>
public virtual ICollection<DTO> GetAll()
{
ICollection<Entity> tList = baseBLL.GetAll();
return tList.MapToList<Entity, DTO>();
}
3、Entity Framework 实体框架结构
基于方便管理的目的,每个模块都可以采用一种固定分层的方式来组织模块的业务内容,每个模块都是以麻雀虽小、五脏俱全的方针实施。实例模块的整个业务逻辑层的项目结构如下所示。

如果考虑使用WCF,那么整体的结构和我之前的混合框架差不多,各个模块的职责基本没什么变化,不过由原先在DAL层分开的各个实现层,变化为各个数据库的Mapping层了,而模型增加了DTO,具体项目结构如下所示。

具体的项目说明如下所示:
|
EFRelationship |
系统的业务模块及接口、数据库访问模块及接口、DTO对象、实体类对象、各种数据库映射Mapping类等相关内容。该模块内容紧密结合Database2Sharp强大代码生成工具生成的代码、各层高度抽象继承及使用泛型支持多数据库。 |
|
EFRelationship.WCFLibrary |
系统的WCF服务的业务逻辑模块,该模块通过引用文件方式,把业务管理逻辑放在一起,方便WCF服务部署及调用。 |
|
EFRelationshipService |
框架WCF服务模块,包括基础服务模块BaseWcf和业务服务模块,他们为了方便,分开管理发布。 |
|
EFRelationship.Caller |
定义了具体业务模块实现的Façade应用接口层,并对Winform调用方式和WCF调用方式进行包装的项目。 |
具体我们以一个会员系统设计为例,它的程序集关系如下所示。

我们来看看整个架构的设计效果如下所示。

其中业务逻辑层模块(以及其它应用层)我们提供了很多基于实体框架的公用类库(WHC.Framework.EF),其中的继承关系我们将它放大,了解其中的继承细节关系,效果如下所示。

上图很好的概述了我的EF实体框架的设计思路,这些层最终还是通过代码生成工具Database2Sharp进行一体化的生成,以提高快速生产的目的,并且统一所有的命名规则。后面有机会再写一篇随笔介绍代码生成的逻辑部分。
上图左边突出的两个工厂类,一个IFactory是基于本地直连方式,也就是直接使用EF框架的对象进行处理;一个CallerFactory是基于Facade层实现的接口,根据配置指向WCF数据服务对象,或者直连对象进行数据的操作处理。
这个系列文章如下所示:
Entity Framework 实体框架的形成之旅--基于泛型的仓储模式的实体框架(1)
Entity Framework 实体框架的形成之旅--利用Unity对象依赖注入优化实体框架(2)
Entity Framework 实体框架的形成之旅--基类接口的统一和异步操作的实现(3)
Entity Framework 实体框架的形成之旅--实体数据模型 (EDM)的处理(4)
Entity Framework 实体框架的形成之旅--Code First的框架设计(5)
Entity Framework 实体框架的形成之旅--Code First模式中使用 Fluent API 配置(6)
Entity Framework 实体框架的形成之旅--数据传输模型DTO和实体模型Entity的分离与联合
Entity Framework 实体框架的形成之旅--数据传输模型DTO和实体模型Entity的分离与联合的更多相关文章
- Entity Framework 实体框架的形成之旅--实体框架的开发的几个经验总结
在前阵子,我对实体框架进行了一定的研究,然后把整个学习的过程开了一个系列,以逐步深入的方式解读实体框架的相关技术,期间每每碰到一些新的问题需要潜入研究.本文继续前面的主题介绍,着重从整体性的来总结一下 ...
- Entity Framework 实体框架的形成之旅--为基础类库接口增加单元测试,对基类接口进行正确性校验(10)
本篇介绍Entity Framework 实体框架的文章已经到了第十篇了,对实体框架的各个分层以及基类的封装管理,已经臻于完善,为了方便对基类接口的正确性校验,以及方便对以后完善或扩展接口进行回归测试 ...
- Entity Framework 实体框架的形成之旅--几种数据库操作的代码介绍(9)
本篇主要对常规数据操作的处理和实体框架的处理代码进行对比,以便更容易学习理解实体框架里面,对各种数据库处理技巧,本篇介绍几种数据库操作的代码,包括写入中间表操作.联合中间表获取对象集合.递归操作.设置 ...
- Entity Framework 实体框架的形成之旅--界面操作的几个典型的处理(8)
在上篇随笔<Entity Framework 实体框架的形成之旅--数据传输模型DTO和实体模型Entity的分离与联合>里面,介绍了在Entity Framework 实体框架里面引入了 ...
- Entity Framework 实体框架的形成之旅--实体数据模型 (EDM)的处理(4)
在前面几篇关于Entity Framework 实体框架的介绍里面,已经逐步对整个框架进行了一步步的演化,以期达到统一.高效.可重用性等目的,本文继续探讨基于泛型的仓储模式实体框架方面的改进优化,使我 ...
- Entity Framework 实体框架的形成之旅--Code First模式中使用 Fluent API 配置(6)
在前面的随笔<Entity Framework 实体框架的形成之旅--Code First的框架设计(5)>里介绍了基于Code First模式的实体框架的经验,这种方式自动处理出来的模式 ...
- Entity Framework 实体框架的形成之旅--Code First的框架设计(5)
在前面几篇介绍了Entity Framework 实体框架的形成过程,整体框架主要是基于Database First的方式构建,也就是利用EDMX文件的映射关系,构建表与表之间的关系,这种模式弹性好, ...
- Entity Framework 实体框架的形成之旅--基类接口的统一和异步操作的实现(3)
在本系列的第一篇随笔<Entity Framework 实体框架的形成之旅--基于泛型的仓储模式的实体框架(1)>中介绍了Entity Framework 实体框架的一些基础知识,以及构建 ...
- Entity Framework 实体框架的形成之旅--利用Unity对象依赖注入优化实体框架(2)
在本系列的第一篇随笔<Entity Framework 实体框架的形成之旅--基于泛型的仓储模式的实体框架(1)>中介绍了Entity Framework 实体框架的一些基础知识,以及构建 ...
随机推荐
- 为jQuery的$.ajax设置超时时间
jQuery的ajax模块封装了非常强大的功能,有时候我们在发送一个ajax请求的时候希望能有一个超时的时间,想让程序在一段时间请求不到数据时做出一些反馈.幸运的是jQuery为我们提供了这样的参数: ...
- ubuntu 安装 axel
Axel 通过打开多个 HTTP/FTP 连接来将一个文件进行分段下载,从而达到加速下载的目的.对于下载大文件,该工具将特别有用. 安装:sudo apt-get install axel 一般使用: ...
- the bundle at bundle path is not signed using an apple submission certificate
在app上架的时候,出现这个错误,也许只是你的Apple Worldwide Developer Relations Certification Authority Intermediate Cert ...
- NanoProfiler - 适合生产环境的性能监控类库 之 大数据篇
上期回顾 上一期:NanoProfiler - 适合生产环境的性能监控类库 之 基本功能篇 上次介绍了NanoProfiler的基本功能,提到,NanoProfiler实现了MiniProfiler欠 ...
- C struct结构体内存对齐问题
在空间看到别人的疑问引起了我的兴趣,刚好是我感兴趣的话题,就写一下.为了别人的疑问,也发表在qq空间里.因为下班比较晚,10点才到家,发表的也晚.其实是个简单的问题. 直接用实例和内存图说明: #i ...
- HTML学习入门
HTML(元素.属性) HTML: 超文本标记语言 1. 超文本即为带有链接属性的文本 2.标记即为标签 一.body属性: bgcolor:页面背景颜色 text:文字颜色 backgroun ...
- MVVM架构~knockoutjs系列之为Ajax传递Ko数组对象
返回目录 一些要说的 这是一个很有意思的题目,在KO里,有对象和数组对象两种,但这两种对象对外表现都是一个function,如果希望得到他的值,需要进行函数式调用,如ko_a(),它的结果为一个具体值 ...
- Nodejs·内存控制
之前有考虑过Node中的内存管理,但是没想到Node的内存机制与JVM如此相像. 看完这部分的内容,基本可以了解Node中的内存使用技巧: 1 尽量不要做过多的缓存 2 使用队列应该有限制 3 注意全 ...
- fir.im Weekly - 可能是 iOS 审核最全面的解决方案
ipv6 被拒绝,后台定位被拒绝--让很多国内 iOS 开发者心力交瘁.这是一份关于 iOS 审核的终极免费方案,作者iOSWang对最近iOS 审核被拒问题给出了比较全面的方案:Solve-App- ...
- Backbone中 View之间传值的解决办法
Backbone中的View就是用来展示由Model层传出的数据,或者在View里产生的一些数据,包括输入框中输入等产生的数据,由当前View传递到另外一个View层里,应该怎么办呢,我之前读到一位博 ...