结婚虽易,终老不易:EntityFramework和AutoMapper的婚后生活
寫在前面
- 我到底是什么?
- 越界的可怕
- 做好自己
- 后記
上一篇《戀愛(ài)雖易,相處不易:當(dāng)EntityFramework愛(ài)上AutoMapper》文章的最后提到,雖然AutoMapper為了EntityFramework做了一些改變,然后就看似幸福的在一起了,但是有人(Roger Alsing)看不下去,寫了一篇文章:http://rogeralsing.com/2013/12/01/why-mapping-dtos-to-entities-using-automapper-and-entityframework-is-horrible/,文中提到EntityFramework和AutoMapper在一起是可怕的,關(guān)于這篇英文文章,作為3.5級(jí)都考兩次的我來(lái)說(shuō)真是天書,無(wú)奈一邊翻譯一邊猜測(cè)看了幾遍,英文好的同學(xué)可以提供下中文版,因?yàn)槲恼轮杏写a,所以作者表達(dá)的意思還是可以理解一些。
文章標(biāo)題主要關(guān)鍵字:mapping DTOs to Entities,注意并不是“Entities?to?DTOs”,表示實(shí)體對(duì)象到DTO的轉(zhuǎn)換,并做一些CURD操作,代碼看似合理,但是好像又有些不合理,為什么?主要是對(duì)所涉及的概念不清晰,EntityFramework、AutoMapper和DTO,他們到底是什么?有沒(méi)有在一起的可能?請(qǐng)接著往下看。
我到底是什么?
EntityFramework(父親)、AutoMapper(母親)和DTO(孩子)你我都知道的官方定義:
- EntityFramework:是微軟以 ADO.NET 為基礎(chǔ)所發(fā)展出來(lái)的對(duì)象關(guān)系對(duì)應(yīng) (O/R Mapping) 解決方案。
- AutoMapper:Object-Object Mapping工具。
- DTO:數(shù)據(jù)傳輸對(duì)象(Data Transfer Object)。
EntityFramework的定義中有“ORM“關(guān)鍵字,那ORM又是什么?
ORM:對(duì)象關(guān)系映射(Object/Relation Mapping),是隨著面向?qū)ο蟮能浖_發(fā)方法發(fā)展而產(chǎn)生的。面向?qū)ο蟮拈_發(fā)方法是當(dāng)今企業(yè)級(jí)應(yīng)用開發(fā)環(huán)境中的主流開發(fā)方法,關(guān)系數(shù)據(jù)庫(kù)是企業(yè)級(jí)應(yīng)用環(huán)境中永久存放數(shù)據(jù)的主流數(shù)據(jù)存儲(chǔ)系統(tǒng)。對(duì)象和關(guān)系數(shù)據(jù)是業(yè)務(wù)實(shí)體的兩種表現(xiàn)形式,業(yè)務(wù)實(shí)體在內(nèi)存中表現(xiàn)為對(duì)象,在數(shù)據(jù)庫(kù)中表現(xiàn)為關(guān)系數(shù)據(jù)。內(nèi)存中的對(duì)象之間存在關(guān)聯(lián)和繼承關(guān)系,而在數(shù)據(jù)庫(kù)中,關(guān)系數(shù)據(jù)無(wú)法直接表達(dá)多對(duì)多關(guān)聯(lián)和繼承關(guān)系。 ? --百度百科
概念清楚了就好辦了,我們?cè)賮?lái)分析下,從上面定義可以看出:AutoMapper是Object-Object映射工具,EntityFramework是一種ORM框架,DTO是數(shù)據(jù)傳輸對(duì)象(Data Transfer Object),請(qǐng)注意這三個(gè)定義中都包含對(duì)象(Object)關(guān)鍵字,毫無(wú)疑問(wèn),AutoMapper所做的工作就是ORM中的“O”和DTO中的“O”之間的映射轉(zhuǎn)換。
DTO中的“O”比較好理解,就是數(shù)據(jù)傳輸對(duì)象,不包含任何的業(yè)務(wù)邏輯,只是存儲(chǔ)數(shù)據(jù)的一種對(duì)象,用于各層之間的數(shù)據(jù)傳遞。一般的項(xiàng)目都會(huì)采用分層設(shè)計(jì)(也就是常見的三層架構(gòu)),每一層都是一個(gè)相對(duì)內(nèi)聚的設(shè)計(jì),一種松耦合結(jié)構(gòu)。而層與層之間進(jìn)行通訊的就是DTO,而這個(gè)“O”常常不是ORM的O。其實(shí)也可能不是DomainEntity,也不是ViewModel,但是它卻有可能通過(guò)組合、分解等方式進(jìn)行轉(zhuǎn)換。
那ORM中“O”是什么意思?關(guān)于ORM的使用,網(wǎng)上有很多的爭(zhēng)論,拋開性能問(wèn)題,有人提出說(shuō)“ORM注定了業(yè)務(wù)邏輯和數(shù)據(jù)庫(kù)的高度耦合”,我覺(jué)得這種理解是錯(cuò)誤的。ORM的“O”是數(shù)據(jù)對(duì)象,與表等有一定的偶合,但它從架構(gòu)設(shè)計(jì)上來(lái)說(shuō),只是倉(cāng)儲(chǔ)層的內(nèi)聚設(shè)計(jì),與業(yè)務(wù)邏輯無(wú)關(guān)(當(dāng)然現(xiàn)在很多小系統(tǒng)會(huì)用它來(lái)直接替代業(yè)務(wù)邏輯對(duì)象),而真正的業(yè)務(wù)邏輯對(duì)象(按領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)來(lái)說(shuō))是領(lǐng)域?qū)ο?#xff0c;真正的的系統(tǒng)核心對(duì)象是領(lǐng)域?qū)ο?#xff0c;而數(shù)據(jù)對(duì)象是可變的,領(lǐng)域?qū)ο髣t相對(duì)穩(wěn)定,數(shù)據(jù)對(duì)象到領(lǐng)域?qū)ο笫峭ㄟ^(guò)倉(cāng)儲(chǔ)層的適配器來(lái)實(shí)現(xiàn)的。
從上面的結(jié)論中可以看出,ORM中的“O”是數(shù)據(jù)對(duì)象。關(guān)于數(shù)據(jù)對(duì)象,有人說(shuō)數(shù)據(jù)對(duì)象是穩(wěn)定的,那要看數(shù)據(jù)是基于“數(shù)據(jù)”的設(shè)計(jì),還是基于“對(duì)象”的設(shè)計(jì)。如果是基于“對(duì)象”的設(shè)計(jì),那么設(shè)計(jì)之初,就必須把業(yè)務(wù)對(duì)象分析清楚、我們常把它說(shuō)成領(lǐng)域?qū)ο?#xff0c;其實(shí)這個(gè)對(duì)象基本是穩(wěn)定的(至少核心部分是穩(wěn)定,如果核心對(duì)象變了,那是另一個(gè)話題,需求變了),而數(shù)據(jù)對(duì)象可能就不一定了,有可能SqlServer數(shù)據(jù)有的類型其它數(shù)據(jù)庫(kù)沒(méi)有,同樣隨著重構(gòu)的進(jìn)行,為了提高性能,可能會(huì)對(duì)一些表進(jìn)行拆分和組合,原先在一個(gè)表中的數(shù)據(jù),分成了兩個(gè)表,或在視圖中了。但不管數(shù)據(jù)表怎么變化,最終也只是倉(cāng)儲(chǔ)層內(nèi)部的實(shí)現(xiàn)方式變了,當(dāng)然ORM的“O”也會(huì)變,但出了這一層,一切都還是原來(lái)的樣子。
理解這些概念很重要,理解了你會(huì)發(fā)現(xiàn),我為什么把EntityFramework看做“父親”,AutoMapper看做“母親”,DTO看做“孩子”,當(dāng)然這只是某種意義上的關(guān)系比作,只有在這三者結(jié)合才會(huì)出現(xiàn),比如DTO可以脫離EntityFramework和AutoMapper獨(dú)立存在,但他就不是“孩子”的概念了。
越界的可怕
什么叫越界?就是不是你干的事你卻干了,所做的工作超出自己的范圍之外,Roger Alsing的“Horrible”文章我覺(jué)得就是在表達(dá)這個(gè)意思。AutoMapper的工作范圍只是對(duì)象之間的映射轉(zhuǎn)換,也就是說(shuō)EntityFramework中的“數(shù)據(jù)對(duì)象”到“DTO”之間的映射轉(zhuǎn)換,但如果涉及到一些數(shù)據(jù)訪問(wèn)或是操作,這就是AutoMapper的越界行為,因?yàn)檫@些操作并不在她的職責(zé)范圍之內(nèi),而應(yīng)該是EntityFramework所做的工作。
某種意義上,可以看做:AutoMapper(母親)只是EntityFramework(父親)和DTO(孩子)之間的橋梁或是溝通,至于賺錢養(yǎng)家的事就交給EntityFramework(父親)去做,如果AutoMapper(母親)幫助EntityFramework(父親)去賺錢養(yǎng)家,可能會(huì)造成相反的效果,也就是說(shuō)AutoMapper(母親)請(qǐng)做好“全職太太”即可。
我們來(lái)看下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的用法,我們?cè)?#xff1a;http://www.cnblogs.com/xishuai/p/3704435.html#xishuai_h1中有講解,ConstructUsing表示自定義類型轉(zhuǎn)換器,發(fā)生在映射之前,對(duì)映射的操作做一些處理并返回相應(yīng)的目標(biāo)類型,注意這里的處理并不是EntityFramework中的持久化,如果在AutoMapper的自定義類型轉(zhuǎn)換器中做這些操作,就顯得有點(diǎn)不倫不類了。
關(guān)于AutoMapper這樣的“越界行為”,Roger Alsing總結(jié)出了其中的優(yōu)缺點(diǎn),本人就不翻譯了,以免起到誤讀的效果。
優(yōu)點(diǎn):
- Looks simple on paper
- Easy to implement on read side and client side
缺點(diǎn):
- 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
做好自己
如果我們?cè)谏厦娲a中去掉AutoMapper,將會(huì)變得如何?請(qǐng)看下面:
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的代碼形成了明顯的對(duì)比,但如果去掉AutoMapper也就失去了DTO的意義,試想沒(méi)有母親哪來(lái)的孩子?但是如果EntityFramework(父親)、AutoMapper(母親)和DTO(孩子)這三口之家想和諧的生活在一起,那怎么辦?就是AutoMapper只要負(fù)責(zé)對(duì)象映射轉(zhuǎn)換即可,也就是做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幸福的生活下去了,看到這有人又可能有疑問(wèn),上篇中AutoMapper為EntityFramework做的IQueryable擴(kuò)展是不是“越界行為”,注意QueryableExtensions只是AutoMapper所做的擴(kuò)展,并不是代替EntityFramework去完成持久化操作。
如果你覺(jué)得EntityFramework和AutoMapper可以幸福終老,那就瘋狂的“戳”右下角的“推薦”吧。^_^
AutoMapper參考文檔:
- 【AutoMapper官方文檔】DTO與Domin Model相互轉(zhuǎn)換(上)
- 【AutoMapper官方文檔】DTO與Domin Model相互轉(zhuǎn)換(中)
- 【AutoMapper官方文檔】DTO與Domin Model相互轉(zhuǎn)換(下)
轉(zhuǎn)載于:https://www.cnblogs.com/xishuai/p/3720662.html
總結(jié)
以上是生活随笔為你收集整理的结婚虽易,终老不易:EntityFramework和AutoMapper的婚后生活的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 移动设备和SharePoint 2013
- 下一篇: Qt 字符串QString arg()用