2012年12月31日 星期一

ASP.NET MVC - ViewModel 參考文章與其他說明

一開始必須再三地強調,這裡所說的 ViewModel 與 MVVM 所謂的 View Model 是不同的,所以 Sliverlight WPF  XAML 所會用到的 View Model 不在此篇文章的討論範圍內。

請務必先看過前面兩篇有關 ASP.NET MVC - ViewModel 的文章:

ASP.NET MVC 的 ViewModel - 基礎篇

ASP.NET MVC - 為什麼不建議在 ViewModel 裡加入行為

而這一篇將會列出國外一些有提到 ASP.NET MVC - ViewModel 的文章與討論,並且就這些內容提出我的想法。

 


有人說,在 ViewModel 類別裡是否要加入方法會分成兩派……

我會說,管你什麼派,蘋果派還是香蕉芭樂派,加入方法不是不可以,如果是頁面顯示的操作方法,倒是可以接受就是不要加入有關資料存取操作的方法,不要把 ViewModel 當成是 Domain Model 來用!

 

在 CodeClimber 這個 Blog 裡於 2009 -10-27 就有針對 ASP.NET MVC 開發寫了一篇「12 ASP.NET MVC Best Practices(12 個 ASP.NET MVC 最佳實踐)」文章,這篇文章發表的當時為 ASP.NET MVC 1.0 的時候,所以時至今日再來看這篇文章,裡頭有許多個項目是已經過時了,但是其中的第七點還是可具參考。

7 – DomainModel != ViewModel

The DomainModel represents the domain, while the ViewModel is designed around the needs of the View, and these two worlds might be (and usually are) different. Furthermore the DomainModel is data plus behaviours, is hierarchical and is made of complex types, while the ViewModel is just a DTO, flat, and made of strings. To remove the tedious and error-prone object-mapping code, you can use AutoMapper. For a nice overview of the various options I recommend you read: ASP.NET MVC View Model Patterns.

中國那邊也有人將此篇文章翻譯為中文,大家可以作為參考「<译>12个asp.net MVC最佳实践」,以下為第七點的翻譯節錄,

7–DomainModel != ViewModel

DomainModel代表着相应的域,但ViewModel却是为View的需要而创建。这两者之间或许(一般情况下都)是不同的,此外DomainModel是数据加上行为的组合体,是由复杂的变量类型组成的并且具有层次。而ViewModel只是由一些String等简单变量类型组成。如果想移除冗余并且容易导致出错的ORM代码,可以使用AutoMapper.如果想要了解更多,我推荐阅读:ASP.NET MVC View Model Patterns.

這邊先不討論 AutoMapper,有興趣的朋友可以先自行研究,其中有提到的「ASP.NET View Model Patterns」這一篇文章就值得一讀。

Steve Michelotti - ASP.NET MVC View Model Patterns

image

另一篇文章也有討論到 ASP.NET MVC 的 ViewModel,並用一個簡單的例子來做說明,

Rachel Appel on Software Development -  Comparing the MVC and MVVM patterns along with their respective ViewModels.

image

有關 Domain Model 與 ViewModel 的討論,中國的微軟 MVP 「張善友」也有寫了一篇文章,「使用View Model从表现层分离领域模型」,對於兩種模型類別做了詳細的解說。

 

另外有人在 ASP.NET Forums 的 ASP.NET MVC 區提出了一個問題「Should we have methods in view model classes?」,也建議各位可以看看,該版的板主給了一個簡單的 ViewModel 類別定義,此類別中包含了一個 Method,

public class ArticleRating
{
    public int ArticleID { get; set; }
    public string Title { get; set; }
    public int Rating { get; set; }
    public int TotalRaters { get; set; }
    public double AverageRating { get; set; }
 
    public IHtmlString Checked(double lower, double upper) 
    {
        return this.AverageRating > lower && this.AverageRating <= upper 
            ? new HtmlString(" checked=\"checked\"") 
            : null;
    }
}

這個 ViewModel 類別的方法成員是用來處理頁面上的 CheckBox 是否顯示勾選狀態,這個做法是可以當做參考,我們一再強調不要在頁面裡寫入過多的程式邏輯,所以把一些頁面顯示的邏輯判斷程式給抽出來,可以讓頁面更為簡潔,也可以提高可讀性。

如果 View Engine 是使用 Razor 的話,像這類的頁面邏輯判斷就可以改用 Razor Helper 的方式,會比把程式邏輯放在 ViewModel 類別中更為恰當,以下有關 Razor Helper 的文章可以作為參考:

ScottGu's Blog - ASP.NET MVC 3 and the @helper syntax within Razor

天空的垃圾場 - ASP.NET MVC – Razor的@helper語法

CODING 之路,不由分說 - Razor 學習筆記 - @functions 和 @helper

 

其實說了這麼多,我就是要跟各位強調「ViewModel 類別裡不要有行為,尤其是不該把具有資料存取操作的方法給加到 ViewModel 裡」,我們寫的是 ASP.NET MVC 並不是 MVVM,我們已經把行為操作的責任劃分給 Controller,另外也可以再切分 Repository 與 Service 層,讓資料存取與商業邏輯的責任再從 Controoller 給切出去,如此一來就更沒有理由要把這些責任給丟到 ViewModel 去,然後再傳到 View 那邊去拋頭露臉。

切記 MVC 三個部分的權責劃分,並做好系統架構的規劃,如此一來就可以清楚了解 ViewModel 的用意。

 

ViewModel 的簡單應用參考:

CodeProject - ASP.NET MVC 2 Basics - Working with ListBoxes

Mikesdotnetting - View Model Design And Use In Razor Views

 

以上

3 則留言:

  1. Hi Kevin,
    看了你3篇关于viewmodel的文章,受益匪浅,谢谢!有个关于viewmodel的问题想请教。我在repository层用EF进行数据库操作,假设有一个entity model named "UserModel"。如果我只需要简单地把UserModel显示到view page,那你觉得还有必要再为其创建一个对应的UserViewModel class吗? 还是直接把entity model直接传给view?

    如果是要创建viewmodel的话, UserModel和UserViewModel的代码是完全相同的,总给我重复代码的感觉,而且如果是大型的project,entity model太多的话,每个entity model都要再创建一个view model for view page,而且entity model class发生了变化,还要再修改viewmodel class,感觉很难维护。

    想听听你的建议,谢谢!

    回覆刪除
    回覆
    1. 我的建議會是鼓勵使用 ViewModel,而至於你所說到的當 ViewModel 的內容與 Entity Model 的內容是一致的時候,如果專案架構不是很龐大的時候,我本身也是直接使用 Entity Model 而不另外建立 ViewModel,但是專案架構大的時候,我會建立這個 ViewModel,理由是,大專案架構本身有其複雜性,各層間必須要做到有一定程度的隔離,以保持各層的讀利與彈性,中間的物件類別轉換再使用 AutoMapper 來做處理。
      .
      大型專案裡面的類別多,以致於管理上會混亂,但是東西多的混亂在各層裡只要做好管理就可以維護,但是物件類別在各層之間交互亂竄,很容易造成各層的耦合與相互依賴的問題,再說到當類別出現了變化與修改,我想修改類別來說這還算是小事,真正會讓人抓狂的應該是各層方法內的程式修改。
      .
      以上是我的回答內容,這是我的經驗與見解,並非表示這是所謂的正確答案,畢竟程式設計沒有所謂正確答案,而只有最適合的做法。

      刪除
    2. 明白了,謝謝你的講解!

      刪除

提醒

千萬不要使用 Google Talk (Hangouts) 或 Facebook 及時通訊與我聯繫、提問,因為會掉訊息甚至我是過了好幾天之後才發現到你曾經傳給我訊息過,請多多使用「詢問與建議」(在左邊,就在左邊),另外比較深入的問題討論,或是有牽涉到你實作程式碼的內容,不適合在留言板裡留言討論,請務必使用「詢問與建議」功能(可以夾帶檔案),謝謝。