在ASP.NET5文章看到留言,david提到EF7 Code Only。想起之前隱約讀到EF7將有所變革,會只剩下Code First(程式碼先行),當時看了也沒放心上。一經提醒,不對,Code First Only? 花惹發?事情大條了!

Code First的點子很酷,Schema不用預先規劃討論,程式設計師依需求打造資料物件類別,第一次執行時來個初始化程序,對應的資料表就在DB自動建好,寫DB就算寫D槽一樣簡單,好不爽快。

只可惜,實務上事情並不像想像這麼美好。除非是小型系統,資料表侷限少數程式使用,不涉及複雜系統整合且沒有太多報表需求,才可能交由程式設計師全權做主。否則,Schema常是多方協調下的產物。在撰寫程式之前,系統分析人員就得提出Schema規劃供各資料使用單位審閱,檢查是否短缺欄位導致其他系統難以運作?能否滿足轉檔及報表需求?正規化反正規化加不加鍵值索引,要在嚴謹與效能間取得平衡,哪些邏輯要由Procedure、Trigger、View實現,現有設計能否配合?待各界回饋折衝取得共識,訂好Schema像憲法一樣供在那裡,大家不准也不敢改(改了其他系統壞掉,你要出來坦嗎?),各系統的程式設計師乖乖照著寫程式,世界和平~

必須說,在我工作接觸的專案應用,Database First(資料庫先行)才是常態,Code First或許是程式宅宅心中的AKB48,Database First才是柴米油鹽醬醋茶~

那,EF7說將來只有Code First Only是什麼鬼話?(冰斗啦)

查了資料,幸好,是我了解不夠,一切只是誤會一場!

EF 4.1時代推出Code First,標榜可以未建立資料表前先寫資料庫程式,系統可依類別自動建立資料表,藉以與Database First(寫程式前資料表已經建好)區別。有趣的是,EF 6.1起又納入一項新功能,可以依據現成資料表產生原本要手寫的Code First類別(Code first from database),而不像過去只能產生EDMX!

這個新功能讓Code First這個命名變得無比矛盾-依據「現有資料表」建立一個「在建立資料表前就先寫好」的類別? 最後讓Code First變成一個爛名字,First名不符實,一整個尷尬。因此Code-Based Model(程式碼模型,與EDMX這類XML模型相對比)才是較適切的名稱。

換句話說,所謂EF7 Code First Only應該要正名為Code-Based Model Only!最大的意義在於EF7不再支援EDMX,你仍然可為現有資料庫在專案建立對應資料類別,但不再有編輯修改EDMX的圖形化介面、依EDMX再自動產生類別程式碼的步驟。EF開發Team的考量在於:

  1. XML檔不像程式那麼好被合併、解決衝突以及檢閱
  2. 規格異動時可減少同時修改EDMX及程式碼的重複工作
  3. 針對特定資料提供者的特殊需求,EDMX不像程式碼可透過條件化編譯(#if #else)加工
  4. 程式碼比EDMX更容易提供設計階段的錯誤資訊細節
  5. Migration功能-Code-Based Model將可依程式修改產生自動更新Schema的SQL Script(EDMX只能產生建立資料表Script,不能產生異動Schema Script)

餘下一個重要疑問,資料庫Shema異動時要怎麼同步回到程式端?EDMX有「Update model from databse」功能(雖然有時有點兩光,需要手動介入校正),EF6.1雖然能由現資料庫產生Code-Based Model,但遇到Schema更新時採取的對策只有重新產生覆寫掉類別,若類別裡寫了自訂邏輯就慘了。EF7有計劃要加入「不覆寫原有程式就能套有Schema異動」的功能,但實做方式仍在研究中。

我的專案架構很少將Model邏輯直接寫在EF產生的Entity類別,多半會將邏輯抽取出來,另外寫一顆Model類別(或用自製程式自動產生器產生),讀取時傳入Entity轉為Model,新增/修改前一刻轉成Entity。這個設計哲學是將Entity純粹當成資料庫的對應物件,不要沾染程式端的邏輯,而這麼做最大的好處也在於Entity類別不放自訂邏輯,遇到變動時可以隨時刪掉重建再修改Model配合即可。在此一架構下,倒還能接受用砍掉重建的方式處理Schema更新。

補充:上述提到的「Model邏輯」主要指與Entity屬性密切相關的額外運算或檢核,例如:PropC = PropA + PropB、檢核所有欄位是否都有效的bool IsValid()。有時一個Model對一個Entity(Article Model -> Article Entity),也有可能一個Model涉及多個Entity(例如:Order Model -> OrderMaster Entity + OrderItem Entity)。我的想法是從物件導向概念出發,將跟Model有關的邏輯都該封裝在它自己裡面,以追求SoC(關注點分離)。

【結論】

Code First Only是項誤解,EF7真正的大改變是Code-Based Model Only!

拿掉EDMX是一項重大更動,連EF Team都坦誠自己做好被譙的心理準備 XD,也不鼓勵大家急著升級。依我的看法,只能靠砍掉重建實現「Update model from database」功能是Code-Based Model目前較嚴重的缺陷,EF7也尚未提出妥善解決方案。而我習慣另建Model類別加入自訂邏輯,純粹把Entity當成資料表的100%重現,砍掉重建Entity的做法也OK,預期轉用EF7也不會有太大問題,暫且不用擔心日後不得不升級時吐血一升。

【延伸閱讀】


Comments

# by 達Ming

我絕對同意你說法,CodeFirst 的理念是很理想,開始時自建Class, 由EF 連接 DB 建立table . 每次更新只需要修改DB context。但的確實踐上就很困難,我上個月的專案原本會用ASP.NET Identity,但最近放棄了。因為ASP.NET Identity是需要用Code-First的TPH, TPT, and TPC去擴展。(http://blogs.msdn.com/b/alexj/archive/2009/04/15/tip-12-choosing-an-inheritance-strategy.aspx) 若大型專案,有很多FKey的話,就要建立很多Virtual Collection,暈.... 其實(依據「現有資料表」建立一個「在建立資料表前就先寫好」的類別)不矛盾的。主意是Deployment之用,例如一個Release版本的專案,需要安裝部署時建立DB,code-first就大派用場,取代傳統執行SQL (CREATE TABLE XXXX X 100). 我是香港的朋友,看你的Blog都有好幾個年頭,但第一次在你Blog留言。年紀大,開始少寫程式了, @_@

# by Dennis

你好, 雖然有點離題的, 但還是很想請教一下. 哪個 "另外寫一顆Model類別" 大慨會配搭是Repository Pattern吧? 我能理解為 透過Repository 向Entityframework 查詢 Entity轉換為Domain Model回傳嗎?? 因為Entityframework原本能透過IQueryable + where 增加過慮條件(動態產生Query查詢數據庫), 但傳換後, 好像只能GetAll 再加上where(查詢數據庫 取出所有資料轉換後在C#過濾)?

# by Jeffrey

to Dennis, 習慣上我不喜歡將SQL WHERE條件交給呼叫端自由發揮,雖然如此得針對所有需要的查詢事先規劃,決定傳入參數並撰寫查詢邏輯,有新需求還得改版,費事許多,但優點是所有查詢都在我們的掌握下,儘可能做到最佳化,避免呼叫端使用不當查詢條件拖累整台SQL。 你所說的做法,好處就在於將查詢條件開放給呼叫端發揮,寫Repository時較省工,缺點是無法事前掌控產生的SQL查詢,丟出去的是資料庫Entity而非View Model Entity。二者純粹設計哲學不同,沒什麼對錯。

Post a comment