Asp.net-Mvc
為什麼在 MVC 中將實體作為模型傳遞不是一個好主意?
我們正在使用 MVC 2 RC2 開發一個相當大的應用程序,並且我們收到了一些關於我們使用實體框架的延遲載入方式的回饋。
我們只是在控制器中獲取實體並將它們作為模型發送到視圖,這導致視圖程式碼向數據庫詢問我們在其中使用的導航屬性。我們已經閱讀過這個,它似乎不是一個好的設計,但我們想知道為什麼?
你能幫助我們理解這個設計問題嗎?
謝謝!
這裡的主要問題是耦合。模型背後的想法,即“MVC”中的“M”,是它沒有外部依賴。它是您的應用程序的“核心”。一個設計良好的應用架構的依賴樹應該是這樣的:
+----------------------------------------> 意見 | | | | | v 控制器 ----+-> 模型轉換器 -----> 視圖模型 | \ | | \ | v\v 數據訪問 <---- 持久性 --------> 領域模型 | / | / v/ 映射器 ------+現在我意識到僅僅說“這是一個架構,這是你應該使用的”並不完全令人信服,所以讓我解釋一下這裡發生了什麼:
- 控制器收到請求。
- 控制器呼叫某種持久層(即儲存庫)。
- 持久層檢索數據,然後使用映射器映射到域模型。
- 控制器使用轉換器將域模型更改為視圖模型。
- 控制器選擇必要的視圖並將視圖模型應用到它。
那麼,為什麼這麼好?
- 域模型沒有依賴關係。這是一件非常好的事情,這意味著執行驗證、編寫測試等很容易。這意味著您可以在架構中的任何地方更改任何其他內容,並且永遠不會破壞模型。這意味著您可以跨項目重用模型。
- 持久層返回領域模型的實例。這意味著它可以被建模為一個完全抽象的、與平台無關的介面。需要使用持久層的組件(例如控制器)不會承擔任何額外的依賴關係。這對於持久層的依賴注入和可測試性來說是理想的。持久性、數據訪問和映射器的組合可以存在於自己的程序集中。在較大的項目中,您甚至可以進一步解耦映射器並讓它在通用記錄集上執行。
- Controller 只有兩個下游依賴項——領域模型和持久層。模型應該很少更改,因為那是您的業務模型,並且由於持久層是抽象的,因此控制器幾乎不需要更改(添加新操作除外)。
- 視圖依賴於單獨的 UI 模型。這將它們與領域模型的變化隔離開來。這意味著如果您的業務邏輯發生變化,您不需要更改項目中的每個視圖。它允許視圖是“愚蠢的”,因為視圖應該是 - 它們只不過是視圖數據的佔位符。這也意味著使用不同類型的 UI(即智能客戶端應用程序)重新創建視圖應該很簡單,或者切換到不同的視圖引擎(Spark、NHaml 等)
現在,當使用諸如 Linq to SQL 或實體框架之類的 O/R 映射器時,將它們生成的類視為您的域模型非常誘人。它當然看起來像一個域模型,但事實並非如此。為什麼?
- 實體類與您的關係模型相關聯,隨著時間的推移,它可能並且將會與您的領域模型顯著不同;
- 實體類是愚蠢的。很難支持任何復雜的驗證場景或集成任何業務規則。這被稱為貧血域模型。
- 實體類具有隱藏的依賴關係。儘管它們可能看起來是普通的 POCO,但實際上它們可能具有對數據庫的隱藏引用(即延遲載入關聯)。這最終可能導致與數據庫相關的問題冒泡到視圖邏輯,您最不可能正確分析正在發生的事情並進行調試。
- 但最重要的是,“領域模型”不再是獨立的。 它不能存在於具有數據訪問邏輯的任何程序集之外。嗯,它有點可以,如果你真的努力的話,有辦法解決這個問題,但這不是大多數人這樣做的方式,即使你成功了,你會發現域的實際設計model 受限於您的關係模型,特別是 EF 的行為方式。底線是,如果您決定更改持久性模型,您將破壞域模型,而您的域模型是應用程序中幾乎所有其他內容的基礎。
實體框架類不是域模型。它們是數據關係模型的一部分,並且碰巧具有與域模型中的類相同或相似的名稱。但就依賴管理而言,它們是天壤之別。使用從 ORM 工俱生成的類作為域模型只會導致非常脆弱的架構/設計;您對應用程序的幾乎任何部分所做的**每一次更改都會產生一系列可預測和不可預測的級聯效應。
有很多人似乎認為您不需要一個內聚的、獨立的域模型。通常的藉口是(a)它是一個小項目,和/或(b)他們的領域模型實際上沒有任何行為。但是小項目變得很大,業務規則變得(遠)更加複雜,並且一個貧乏或不存在的域模型不是你可以簡單地重構掉的東西。
這實際上是實體模型設計中最隱蔽的特徵。它似乎工作正常,有一段時間了。直到一兩年後,當您淹沒在缺陷報告和變更請求中,同時拼命嘗試拼湊一個真正的域模型時,您才會發現這是一個多大的錯誤。