Entity Framework 實體框架的造成之旅--數據傳輸模型DTO和實體模型Entity的分離與聯合

在使用Entity Framework 實體框架的時候,咱們大多數時候操做的都是實體模型Entity,這個和數據庫操做上下文結合,能夠利用LINQ等各類方便手段,實現起來很是方便,一切看起來很美好。可是若是考慮使用WCF的時候,可能就會碰到不少相關的陷阱或者錯誤了。由於實體模型Entity的對象可能包括了其餘實體的引用,在WCF裏面就沒法進行序列化,出現錯誤;並且基於WCF的時候,可能沒法有效利用Express表達式,沒法直接使用LINQ等問題都一股腦出現了。本文基於上面的種種問題,闡述了個人整個Entity Framework 實體框架的解決思路,而且在其中引入了數據傳輸模型DTO來解決問題,本文主要介紹數據傳輸模型DTO和實體模型Entity的分離與聯合,從而實現咱們通暢、高效的WCF應用框架。html

一、實體模型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 請求上下文(可能因爲服務關閉)所致。有關詳細信息,請參見服務器日誌。app

默認狀況下,Entity Framework爲了支持它的一些高級特性(延遲加載等),默認將自動生成代理類是設置爲true。若是咱們須要禁止自動生成代理類,那麼能夠在數據庫操做上下文DbContext裏面進行處理設置。框架

Configuration.ProxyCreationEnabled = false;

若是設置爲false,那麼WCF服務能夠工做正常,可是實體類對象裏面的其餘對象集合則爲空了,也就是WCF沒法返回這些引用的內容。異步

同時,在Entity Framework框架裏面,這種把實體類貫穿各個層裏面,也是一種不推薦的作法,因爲WCF裏面傳輸的數據都是序列號過的數據,也沒法像本地同樣利用LINQ來實現數據的處理操做的。函數

那麼咱們應該如何構建基於WCF引用的Entity Framework實體框架呢?工具

二、數據傳輸對象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>();
        }

 

三、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的分離與聯合

相關文章
相關標籤/搜索