在使用Entity Framework 实体框架的时候,咱们大多数时候操做的都是实体模型Entity,这个和数据库操做上下文结合,能够利用LINQ等各类方便手段,实现起来很是方便,一切看起来很美好。可是若是考虑使用WCF的时候,可能就会碰到不少相关的陷阱或者错误了。由于实体模型Entity的对象可能包括了其余实体的引用,在WCF里面就没法进行序列化,出现错误;并且基于WCF的时候,可能没法有效利用Express表达式,没法直接使用LINQ等问题都一股脑出现了。本文基于上面的种种问题,阐述了个人整个Entity Framework 实体框架的解决思路,而且在其中引入了数据传输模型DTO来解决问题,本文主要介绍数据传输模型DTO和实体模型Entity的分离与联合,从而实现咱们通畅、高效的WCF应用框架。html
例如,咱们定义的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 请求上下文(可能因为服务关闭)所致。有关详细信息,请参见服务器日志。app
默认状况下,Entity Framework为了支持它的一些高级特性(延迟加载等),默认将自动生成代理类是设置为true。若是咱们须要禁止自动生成代理类,那么能够在数据库操做上下文DbContext里面进行处理设置。框架
Configuration.ProxyCreationEnabled = false;
若是设置为false,那么WCF服务能够工做正常,可是实体类对象里面的其余对象集合则为空了,也就是WCF没法返回这些引用的内容。异步
同时,在Entity Framework框架里面,这种把实体类贯穿各个层里面,也是一种不推荐的作法,因为WCF里面传输的数据都是序列号过的数据,也没法像本地同样利用LINQ来实现数据的处理操做的。函数
那么咱们应该如何构建基于WCF引用的Entity Framework实体框架呢?工具
前面介绍了直接利用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>(); }
基于方便管理的目的,每一个模块均可以采用一种固定分层的方式来组织模块的业务内容,每一个模块都是以麻雀虽小、五脏俱全的方针实施。实例模块的整个业务逻辑层的项目结构以下所示。
若是考虑使用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)