POCO真那麼重要麼?

不斷聽timiil向我介紹Entity Framework 4.0的誘人之處。當然,他知道我最需要的是穩定且實用的設計時和提供給我從模型到數據庫的設計體驗,這兩點是EF1.0不能滿足我的。之所以我特別在意這兩點,是爲了滿足我經常性修改模型的現實要求。顯然,EF 4似乎有所改善,但顯然離我的要求仍然有相當大的距離。
剛看到的一篇介紹EF4的文章,似乎比較喜歡POCO,這讓我相當意外。那麼,POCO真那麼重要麼?
我不知道除我以外還有誰在實際項目中用過ADO.NET Entity Framework,我用過,從一開始的飽受折磨直到想放棄,到後來越來越令人舒服,過程是漫長了一點,但我覺得這個過程還是值得的。在我的項目中,大約有60+個實體類,繼承深度最深的地方是三層,可以參見實體圖(擔心你看不清,但是又不知道如何上傳1:1的圖):
從中間那個PointBase看開去,可以發現一共有15個派生類。這也是迫不得已的事情。地球人都知道,我一定會被可惡的edmx困擾。確實是這樣,特別是當模型即將發生變化的時候,往往就是噩夢的開始。因爲在EF1環境下我只能手工修改數據庫,然後再來更新模型。僅僅把類似Point實體集名稱修改成Points就得花費我相當多的時間,更別說這些複雜的繼承和關聯關係了。我甚至動過念頭,自己寫一個Visual Studio插件來維護edmx。當然,到後來我發明了“局部固定法”,能夠很輕鬆地控制這個過程了,我甚至可以純手工修改edmx文件而不需要用EF的設計時來代勞。
無論如何,ADO.NET Entity Framework有兩點讓我相當滿意而且目前暫時無法被替代:
1.一個相當穩定且高效的運行時,這方面從一開始都沒有給我製造過麻煩,只是剛開始時有些不熟練需要不斷摸索,並且需要經常性地向timiil討教;
2.Linq不僅給我省去了寫SQL的煩惱,令我合併或分拆查詢因子極爲方便,還可通過傳遞Expression用於靈活地控制業務關係,沒有任何一處需要硬編碼。
我不知道那個噁心的HQL是如何進行查詢因子的合併和分拆的,在我看來根本就是開倒車,非常困惑那些叫好聲是基於什麼,難道都是人云亦云麼?
花點時間舉例解釋一下“查詢因子的合併和分拆”吧。用戶登錄進來後,會建立一個安全上下文存放在Session中,一是限定這個用戶所在的領域和區域(領域就是水環境、大氣環境或噪聲環境;區域就是行政區域-某省或某市了) ;二是這個用戶有很多的個性設置,例如排序習慣、顯示欄目設置等。這個用戶的每一個查詢最終被執行的時候必須受當前業務限定的靜態查詢條件、當前操作中的動態查詢條件和安全上下文因素的三重控制。大部分情況下,這些因素是不確定的,是一個可以任意配置的。這些衆多的因素有些是取並集,有一些是取交集,我習慣上將這兩個過程分別稱爲查詢因子的合併和分拆。
我想說的是,我從來沒有希望EF支持POCO,這個所謂的POCO根本就是僞命題。你希望維持實體類不被污染的原因無非就是傳來傳去對吧?當你需要的時候,你直接另外再寫一個DTO不行麼?依據我長期使用ORM的經驗,透明地實現POCO到數據庫你會得不償失。由框架來完成的數據庫設計通常是相當潦草,慘不忍睹,五年前我使用ECO的時候便深受其害。這是其一。通過手寫的POCO來“再”映射實體對象,可有效地隔離對象模型和數據模型,令業務清晰可辨。例如,經度和緯度在數據庫中可以用一個float來表示,而在對象空間,我希望是另外一個struct,一是可以ToString()成“E116°55′03″”或者“N23°40′17″”的樣子,二是可以在對象空間簡單地做一個對double的隱式類型轉換的重載。顯然,這樣會令你保持足夠的靈活度,而你卻不能指望你的框架能有效地識別你的的這些精心設計,並在數據庫中體現。這是其二。有一個規則,在業務構建過程中,你永遠都不要傳遞對象集合,而是傳遞一個Request對象,例如Expression。這些Request不到最後一刻是不會連接數據庫的。就這個規則來說,真正需要傳遞的實體對象相當少,即使需要傳遞,你所需要傳遞的對象通常是需要通過另外一道封裝的,與實體對象相差甚遠。即使EF4提供了POCO,我也絕對不會傳遞任何實體對象的。這是其三。
那麼我需要什麼?
一個改良的edmx的設計器(相比運行時,我通常叫設計時),在這個設計器中我可以加各種標記,按我的標記來生成數據庫。永遠不要從數據庫更新模型,而是從模型更新數據庫,且更新的時候不能破壞我已有的測試數據。象繼承關係這些複雜操作可以很快解決,而不是象現在需要四步才能完成。這是其一。EF1的QueryableProvider功能還是弱了一些,對表達式的識別能力相當有限,並且無法由開發者自己簡單地擴充。所以經常會拋出一些NotSupportedException,你不得不想一些BT的方式來繞開或者分解成多步。象
context.DataEntries.Where(e => e.Times == times && ((Point)e.PointBase).Name == pointName && e.MonitoringItem.Name == monitoringItemName)
.Select(e => e.LowestCase.HasValue ? e.LowestCase / 2 : e.Average).Average().Value
這樣的語句,我認爲是非常常見的,應該予以支持。 這是其二。
總而言之、統而言之,切切實實提升生產力纔是王道。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章