Entity Framework 實(shí)體框架的形成之旅--數(shù)據(jù)傳輸模型DTO和實(shí)體模型Entity的分離與聯(lián)合

在使用Entity Framework 實(shí)體框架的時(shí)候崎溃,我們大多數(shù)時(shí)候操作的都是實(shí)體模型Entity,這個(gè)和數(shù)據(jù)庫(kù)操作上下文結(jié)合蠕蚜,可以利用LINQ等各種方便手段梢灭,實(shí)現(xiàn)起來非常方便,一切看起來很美好幕侠。但是如果考慮使用WCF的時(shí)候帝美,可能就會(huì)碰到很多相關(guān)的陷阱或者錯(cuò)誤了。因?yàn)閷?shí)體模型Entity的對(duì)象可能包括了其他實(shí)體的引用晤硕,在WCF里面就無(wú)法進(jìn)行序列化悼潭,出現(xiàn)錯(cuò)誤;而且基于WCF的時(shí)候舞箍,可能無(wú)法有效利用Express表達(dá)式舰褪,無(wú)法直接使用LINQ等問題都一股腦出現(xiàn)了。本文基于上面的種種問題疏橄,闡述了我的整個(gè)Entity Framework 實(shí)體框架的解決思路占拍,并且在其中引入了數(shù)據(jù)傳輸模型DTO來解決問題,本文主要介紹數(shù)據(jù)傳輸模型DTO和實(shí)體模型Entity的分離與聯(lián)合捎迫,從而實(shí)現(xiàn)我們通暢晃酒、高效的WCF應(yīng)用框架。

1窄绒、實(shí)體模型Entity無(wú)法在WCF中序列化

例如贝次,我們定義的Entity Framework 實(shí)體類里面包含了其他對(duì)象的引用,例如有一個(gè)Role對(duì)象彰导,有和其他表的關(guān)聯(lián)關(guān)系的蛔翅,默認(rèn)使用傳統(tǒng)方式,在實(shí)體類里面添加[DataContract]方式位谋。

/// <summary>
/// 角色
/// </summary>
[DataContract(IsReference = true)]
public class Role
{ 
    /// <summary>
    /// 默認(rèn)構(gòu)造函數(shù)(需要初始化屬性的在此處理)
    /// </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服務(wù)接口里面使用代碼如下所示山析。

public class Service1 : IService1
{
    public List<Role> GetAllRoles()
    {
        return IFactory.Instance<IRoleBLL>().GetAll().ToList();
    }

     .........

那么我們?cè)赪CF里面使用的時(shí)候,會(huì)得到下面的提示倔幼。
接收對(duì) http://localhost:11229/Service1.svc 的 HTTP 響應(yīng)時(shí)發(fā)生錯(cuò)誤盖腿。這可能是由于服務(wù)終結(jié)點(diǎn)綁定未使用 HTTP 協(xié)議造成的。這還可能是由于服務(wù)器中止了 HTTP 請(qǐng)求上下文(可能由于服務(wù)關(guān)閉)所致损同。有關(guān)詳細(xì)信息翩腐,請(qǐng)參見服務(wù)器日志。
默認(rèn)情況下膏燃,Entity Framework為了支持它的一些高級(jí)特性(延遲加載等)茂卦,默認(rèn)將自動(dòng)生成代理類是設(shè)置為true。如果我們需要禁止自動(dòng)生成代理類组哩,那么可以在數(shù)據(jù)庫(kù)操作上下文DbContext里面進(jìn)行處理設(shè)置等龙。

Configuration.ProxyCreationEnabled = false;

如果設(shè)置為false,那么WCF服務(wù)可以工作正常处渣,但是實(shí)體類對(duì)象里面的其他對(duì)象集合則為空了,也就是WCF無(wú)法返回這些引用的內(nèi)容蛛砰。
同時(shí)罐栈,在Entity Framework框架里面,這種把實(shí)體類貫穿各個(gè)層里面泥畅,也是一種不推薦的做法荠诬,由于WCF里面?zhèn)鬏數(shù)臄?shù)據(jù)都是序列號(hào)過的數(shù)據(jù),也無(wú)法像本地一樣利用LINQ來實(shí)現(xiàn)數(shù)據(jù)的處理操作的位仁。
那么我們應(yīng)該如何構(gòu)建基于WCF引用的Entity Framework實(shí)體框架呢柑贞?

2、數(shù)據(jù)傳輸對(duì)象DTO的引入

前面介紹了直接利用Entity Framework實(shí)體類對(duì)象的弊端聂抢,并且如果是一路到底都使用這個(gè)實(shí)體類钧嘶,里面的很多對(duì)象引用都是空的,對(duì)我們?cè)诮缑鎸邮褂貌槐懔帐瑁乙部赡芤l(fā)了很多WCF框架里面的一些相關(guān)問題有决。
我們根據(jù)上面的問題,引入了一個(gè)DTO(數(shù)據(jù)傳輸對(duì)象)的東西轿亮。
數(shù)據(jù)傳輸對(duì)象(DTO)是沒有行為的POCO對(duì)象疮薇,它的目的只是為了對(duì)領(lǐng)域?qū)ο筮M(jìn)行數(shù)據(jù)封裝,實(shí)現(xiàn)層與層之間的數(shù)據(jù)傳遞我注,界面表現(xiàn)層與應(yīng)用層之間是通過數(shù)據(jù)傳輸對(duì)象(DTO)進(jìn)行交互的按咒。數(shù)據(jù)傳輸對(duì)象DTO本身并不是業(yè)務(wù)對(duì)象,數(shù)據(jù)傳輸對(duì)象是根據(jù)UI的需求進(jìn)行設(shè)計(jì)的但骨。
這個(gè)對(duì)象和具體數(shù)據(jù)存儲(chǔ)的實(shí)體類是獨(dú)立的励七,它可以說是實(shí)體類的一個(gè)映射體,名稱可以和實(shí)體類不同奔缠,屬性數(shù)量也可以實(shí)體類不一致掠抬。那么既然在實(shí)體對(duì)象層外引入了另外一個(gè)DTO對(duì)象層,那么相互轉(zhuǎn)換肯定是避免不了的了校哎,我們?yōu)榱吮苊馐止さ挠成浞绞搅讲ǎ肓肆硗庖粋€(gè)強(qiáng)大的自動(dòng)化映射的工具AutoMapper,來幫助我們快速闷哆、高效腰奋、智能的實(shí)現(xiàn)兩個(gè)層對(duì)象的映射處理。



AutoMapper的使用比較簡(jiǎn)單抱怔,一般如果對(duì)象屬性一直劣坊,他們會(huì)實(shí)現(xiàn)屬性自動(dòng)映射了,如下所示屈留。

Mapper.CreateMap<RoleInfo, Role>();

如果兩者的屬性名稱不一致局冰,那么可以通過ForMember方式指定测蘑,類似下面代碼所示。

AutoMapper.Mapper.CreateMap<BlogEntry, BlogPostDto>() 
  .ForMember(dto => dto.PostId, opt => opt.MapFrom(entity => entity.ID));

AutoMapper也可以把映射信息寫到一個(gè)類里面康二,然后統(tǒng)一進(jìn)行加載碳胳。

Mapper.Initialize(cfg => { cfg.AddProfile<OrganizationProfile>();});

那么基于上面的圖示模式,由于我們采用代碼生成工具自動(dòng)生成的DTO和Entity赠摇,他們屬性名稱是保持一致的固逗,那么我們只需要在應(yīng)用層對(duì)它們兩者對(duì)象進(jìn)行相互映射就可以了浅蚪。

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>();
    }
}

基于這個(gè)內(nèi)部對(duì)接的映射關(guān)系藕帜,我們就可以在Facade接口層提供統(tǒng)一的DTO對(duì)象服務(wù),而業(yè)務(wù)邏輯層(也就是利用Entity Framework 實(shí)體框架的處理成)則依舊使用它的Entity對(duì)象來傳遞惜傲。下面我提供幾個(gè)封裝好的基類接口供了解DTO和Entity的相互銜接處理洽故。

1)傳入DTO對(duì)象,并轉(zhuǎn)換為Entity對(duì)象盗誊,使用EF對(duì)象插入时甚。

/// <summary>
/// 插入指定對(duì)象到數(shù)據(jù)庫(kù)中
/// </summary>
/// <param name="dto">指定的對(duì)象</param>
/// <returns>執(zhí)行成功返回<c>true</c>,否則為<c>false</c></returns>
public virtual bool Insert(DTO dto)
{
    Entity t = dto.MapTo<Entity>();
    return baseBLL.Insert(t);
}

2)根據(jù)條件從EF框架中獲取Entity對(duì)象哈踱,并轉(zhuǎn)換后返回DTO對(duì)象

/// <summary>
/// 查詢數(shù)據(jù)庫(kù),返回指定ID的對(duì)象
/// </summary>
/// <param name="id">ID主鍵的值</param>
/// <returns>存在則返回指定的對(duì)象,否則返回Null</returns>
public virtual DTO FindByID(object id)
{
    Entity t = baseBLL.FindByID(id);
    return t.MapTo<DTO>();
}

3)根據(jù)條件從EF框架中獲取Entity集合對(duì)象荒适,并轉(zhuǎn)換為DTO列表對(duì)象

/// <summary>
/// 返回?cái)?shù)據(jù)庫(kù)所有的對(duì)象集合
/// </summary>
/// <returns></returns>
public virtual ICollection<DTO> GetAll()
{
    ICollection<Entity> tList = baseBLL.GetAll();
    return tList.MapToList<Entity, DTO>();
}

3、Entity Framework 實(shí)體框架結(jié)構(gòu)

基于方便管理的目的开镣,每個(gè)模塊都可以采用一種固定分層的方式來組織模塊的業(yè)務(wù)內(nèi)容刀诬,每個(gè)模塊都是以麻雀雖小、五臟俱全的方針實(shí)施邪财。實(shí)例模塊的整個(gè)業(yè)務(wù)邏輯層的項(xiàng)目結(jié)構(gòu)如下所示陕壹。



如果考慮使用WCF,那么整體的結(jié)構(gòu)和我之前的混合框架差不多树埠,各個(gè)模塊的職責(zé)基本沒什么變化糠馆,不過由原先在DAL層分開的各個(gè)實(shí)現(xiàn)層,變化為各個(gè)數(shù)據(jù)庫(kù)的Mapping層了怎憋,而模型增加了DTO又碌,具體項(xiàng)目結(jié)構(gòu)如下所示。



具體的項(xiàng)目說明如下所示:
EFRelationship

系統(tǒng)的業(yè)務(wù)模塊及接口绊袋、數(shù)據(jù)庫(kù)訪問模塊及接口毕匀、DTO對(duì)象、實(shí)體類對(duì)象愤炸、各種數(shù)據(jù)庫(kù)映射Mapping類等相關(guān)內(nèi)容期揪。該模塊內(nèi)容緊密結(jié)合Database2Sharp強(qiáng)大代碼生成工具生成的代碼、各層高度抽象繼承及使用泛型支持多數(shù)據(jù)庫(kù)规个。

EFRelationship.WCFLibrary
系統(tǒng)的WCF服務(wù)的業(yè)務(wù)邏輯模塊凤薛,該模塊通過引用文件方式姓建,把業(yè)務(wù)管理邏輯放在一起,方便WCF服務(wù)部署及調(diào)用缤苫。

EFRelationshipService
框架WCF服務(wù)模塊速兔,包括基礎(chǔ)服務(wù)模塊BaseWcf和業(yè)務(wù)服務(wù)模塊,他們?yōu)榱朔奖慊盍幔珠_管理發(fā)布涣狗。

EFRelationship.Caller
定義了具體業(yè)務(wù)模塊實(shí)現(xiàn)的Fa?ade應(yīng)用接口層,并對(duì)Winform調(diào)用方式和WCF調(diào)用方式進(jìn)行包裝的項(xiàng)目舒憾。

具體我們以一個(gè)會(huì)員系統(tǒng)設(shè)計(jì)為例镀钓,它的程序集關(guān)系如下所示。



我們來看看整個(gè)架構(gòu)的設(shè)計(jì)效果如下所示镀迂。



其中業(yè)務(wù)邏輯層模塊(以及其它應(yīng)用層)我們提供了很多基于實(shí)體框架的公用類庫(kù)(WHC.Framework.EF)丁溅,其中的繼承關(guān)系我們將它放大,了解其中的繼承細(xì)節(jié)關(guān)系探遵,效果如下所示窟赏。

上圖很好的概述了我的EF實(shí)體框架的設(shè)計(jì)思路,這些層最終還是通過代碼生成工具Database2Sharp進(jìn)行一體化的生成箱季,以提高快速生產(chǎn)的目的涯穷,并且統(tǒng)一所有的命名規(guī)則。后面有機(jī)會(huì)再寫一篇隨筆介紹代碼生成的邏輯部分藏雏。
上圖左邊突出的兩個(gè)工廠類拷况,一個(gè)IFactory是基于本地直連方式,也就是直接使用EF框架的對(duì)象進(jìn)行處理诉稍;一個(gè)CallerFactory是基于Facade層實(shí)現(xiàn)的接口蝠嘉,根據(jù)配置指向WCF數(shù)據(jù)服務(wù)對(duì)象,或者直連對(duì)象進(jìn)行數(shù)據(jù)的操作處理杯巨。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蚤告,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子服爷,更是在濱河造成了極大的恐慌杜恰,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仍源,死亡現(xiàn)場(chǎng)離奇詭異心褐,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)笼踩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門逗爹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人嚎于,你說我怎么就攤上這事掘而⌒冢” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵袍睡,是天一觀的道長(zhǎng)知染。 經(jīng)常有香客問我,道長(zhǎng)斑胜,這世上最難降的妖魔是什么控淡? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮止潘,結(jié)果婚禮上掺炭,老公的妹妹穿的比我還像新娘。我一直安慰自己覆山,他們只是感情好竹伸,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著簇宽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吧享。 梳的紋絲不亂的頭發(fā)上魏割,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音钢颂,去河邊找鬼钞它。 笑死,一個(gè)胖子當(dāng)著我的面吹牛殊鞭,可吹牛的內(nèi)容都是我干的遭垛。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼操灿,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼锯仪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起趾盐,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤庶喜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后救鲤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體久窟,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年本缠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了斥扛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡丹锹,死狀恐怖稀颁,靈堂內(nèi)的尸體忽然破棺而出队他,到底是詐尸還是另有隱情,我是刑警寧澤峻村,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布麸折,位于F島的核電站,受9級(jí)特大地震影響粘昨,放射性物質(zhì)發(fā)生泄漏垢啼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一张肾、第九天 我趴在偏房一處隱蔽的房頂上張望芭析。 院中可真熱鬧,春花似錦吞瞪、人聲如沸馁启。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)惯疙。三九已至,卻和暖如春妖啥,著一層夾襖步出監(jiān)牢的瞬間霉颠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工荆虱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蒿偎,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓怀读,卻偏偏與公主長(zhǎng)得像诉位,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子菜枷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容