2012年12月22日 星期六

ASP.NET MVC 的 ViewModel - 基礎篇

有鑑於蠻多初學 ASP.NET MVC 開發人員對 ViewModel 的不了解,甚至於有些公司雖標榜採用 ASP.NET MVC 開發專案,但卻沒有詳細規範並定義 ViewModel 的使用方式而產生很多的誤用、亂用、隨便用的亂象,所以對 ViewModel 做個說明。

 


首先要說的是,ASP.NET MVC 的 ViewModel 不同於「MVVM(Model – View – ViewModel)」架構下的 ViewModel,所以有關 Silverlight, WPF 所謂的 ViewModel 並不在說明的範圍內,也不要留言問我這兩個應用的 ViewModel 相關問題,因為我不熟。

 

View 與 Model

在 ASP.NET MVC 的開發,通常一個 View 的 Model 只能使用一種 Model 類別,例如以下的範例,

顯示一筆資料:

image

image

image

 

顯示很多筆資料:

image

image

image

 

但如果我希望在一個頁面上去同時顯示產品資料以及這個分類底下的所有產品的資料時,應該怎麼做呢?

相信初學 ASP.NET MVC 的開發者都會在這裡給卡住,因為一個 View 只能使用一個 Model 類別,無法指定多個 Model 類別的資料給 View,有的開發者就會用以下的方式來處理,

image

如上圖所示,在 Controller Action 中還是一樣回傳一個 Model 類別的資料,但是分類所屬的 Products 則是放在ViewBag(或是 ViewData)內,這樣一來前端 View 就可以拿到資料來使用,如下:

   1: @model ViewModelSample.Models.Categories
   2:  
   3: @{
   4:     ViewBag.Title = "DetailsTest";
   5:     Layout = "~/Views/Shared/_BootstrapLayout.cshtml";
   6: }
   7:  
   8: <h2>Category Details - Test</h2>
   9: <div class="form well">
  10:     <fieldset>
  11:         <legend>Categories</legend>
  12:  
  13:         <div class="control-group">
  14:             <div class="display-label">
  15:                 <strong>@Html.DisplayNameFor(model => model.CategoryID)</strong>
  16:             </div>
  17:             <div class="display-field">
  18:                 @Html.DisplayFor(model => model.CategoryID)
  19:             </div>
  20:         </div>
  21:  
  22:         <div class="control-group">
  23:             <div class="display-label">
  24:                 <strong>@Html.DisplayNameFor(model => model.CategoryName)</strong>
  25:             </div>
  26:             <div class="display-field">
  27:                 @Html.DisplayFor(model => model.CategoryName)
  28:             </div>
  29:         </div>
  30:  
  31:         <div class="control-group">
  32:             <div class="display-label">
  33:                 <strong>@Html.DisplayNameFor(model => model.Description)</strong>
  34:             </div>
  35:             <div class="display-field">
  36:                 @Html.DisplayFor(model => model.Description)
  37:             </div>
  38:         </div>
  39:     </fieldset>
  40: </div>
  41: <div class="form-actions">
  42:     @Html.ActionLink("Edit", "Edit", new { id = Model.CategoryID }, new { @class = "btn" })
  43:     @Html.ActionLink("Back to List", "Index", null, new { @class = "btn" })
  44: </div>
  45:  
  46: <h3>Category's Products</h3>
  47: <table class="table table-striped table-bordered table-hover">
  48:     <tr style="background-color: #E8EEF4;">
  49:         <td>ProductID</td>
  50:         <td>ProductName</td>
  51:         <td>SupplierID</td>
  52:         <td>CategoryID</td>
  53:         <td>QuantityPerUnit</td>
  54:         <td>UnitPrice</td>
  55:         <td>UnitsInStock</td>
  56:         <td>UnitsOnOrder</td>
  57:         <td>ReorderLevel</td>
  58:     </tr>
  59:     @{ 
  60:         var productModels = (IEnumerable<ViewModelSample.Models.Products>)ViewBag.Products; 
  61:     }
  62:     @foreach (var item in productModels)
  63:     {
  64:         <tr>
  65:             <td>
  66:                 @Html.DisplayFor(modelItem => item.ProductID)
  67:             </td>
  68:             <td>
  69:                 @Html.DisplayFor(modelItem => item.ProductName)
  70:             </td>
  71:             <td>
  72:                 @Html.DisplayFor(modelItem => item.SupplierID)
  73:             </td>
  74:             <td>
  75:                 @Html.DisplayFor(modelItem => item.CategoryID)
  76:             </td>
  77:             <td>
  78:                 @Html.DisplayFor(modelItem => item.QuantityPerUnit)
  79:             </td>
  80:             <td>
  81:                 @Html.DisplayFor(modelItem => item.UnitPrice)
  82:             </td>
  83:             <td>
  84:                 @Html.DisplayFor(modelItem => item.UnitsInStock)
  85:             </td>
  86:             <td>
  87:                 @Html.DisplayFor(modelItem => item.UnitsOnOrder)
  88:             </td>
  89:             <td>
  90:                 @Html.DisplayFor(modelItem => item.ReorderLevel)
  91:             </td>
  92:         </tr>
  93:     }
  94: </table>
  95:  

這樣看似好像解決了問題,但如果這個頁面要顯示的資料很多,而且都是分屬在不同的 Model 類別中,那麼 Controller Action 裡不就要一堆的 ViewBag or ViewData 裝資料,然後前端 View 在看要用什麼資料再從 ViewBag(ViewData)裡拿出來,但這樣的作法並不好,資料類型少還可以這樣做,但是資料類型一多,光是一個 View 就要做這麼多的處理,如果有很多的 View 都有這樣的需求時,我想再多的耐心與毅力都是不夠的,有關上面的應用探討,在 demo 的部落格裡就有這麼一篇文章在說這樣的狀況「ASP.NET MVC ViewModel不可遺忘的好幫手」。

 

ViewModel

就如一開始有說過的,ViewModel 與 Silverlight, WPF 所謂的 viewmodel 是不同的,在 MVC 的 ViewModel 就是給 View 用的 Model,因為 View 只能給一種 Model 類別,那麼為了可以讓 View 可以有多種的 Model 類別,所以可以另外建立一個類別,這個類別內就可以建立不同類別的屬性(property,就是不要用 Field),而這一個類別是一個裝載資料用的並且只有給 View 來使用,所以不要在這個 ViewModel 類別中去建立任何的行為(也就是可以做資料處理的方法),也不要給任何的 repository or service。

ViewModel 不是什麼艱深的方法或是 Pattern,它就是簡單的類別而已,只有屬性不必建立方法,總之就是盡量的單純而且不要有任何的行為定義。

也許有人還是一頭霧水,所以我們用上面的例子來實際操作一次。

 

Step.1

新增一個「類別」,這個類別的名稱通常會以「ViewModel」做結尾字,而字首會以 Controller + Action 或是 Controller + View 名稱來命名,

SNAGHTML9bd06b

而建立的 ViewModel 類別檔案會放在 ViewModels 目錄裡,如果預期專案規模會比較大且 ViewModel 會建立很多個時,會建議在 ViewModels 目錄下再依據 Controller 的不同分別建立子目錄,就如同 Views 目錄下的子目錄,依據 Controller 的名稱來建立子目錄,

image

 

Step.2

在 Category Detail 頁面中除了顯示分類資料外也要顯示這個分類的產品資料,所以我們就建立一個 ViewModel 類別,這個類別的屬性分別是一個 Category 型別,另一個屬性的型別則是 IEnumerable<Products>,

image

注意喔!不要在 ViewModel 類別裡加入行為,或是加入 repository or service,這是因為 ViewModel 只是給 View 使用,只是作為資料的傳遞,不希望因為 ViewModel 裡有行為或是可操作資料存取或是商業邏輯操作的媒介出現在 View 裡,這是避免 View 也做資料處理或是流程控制的動作,之前有看到很多人為了貪圖方便,然後在 ViewModel 類別裡建立方法或是加入 repository,就是想要在 View 裡去「寫程式」,太多的程式邏輯在 View 裡面並不恰當也不對,完全違反了 MVC 的基本原則。

 

Step.3

接著就是在 Controller Action 使用這個 ViewModel,

image

 

Step.4

建立 View,要注意的是在「建立強型別檢視」時所選用的「模型類別」要選擇剛才所建立的 CategoryDetailsViewModel,

image

另外,「Scaffold 樣板」可以選擇 Details 或 Empty,因為是自定的資料類別,所以 View 無法直接套用 Scaffold 樣板。

 

Step.5

新加入的 View 所使用的 Model 為我們所建立的 CategoryDetailsViewModel,而 View 的內容並沒有如使用資料實體模型的類別一樣可以直接套用 Scaffold 樣板,所以就必須自己編輯 View 的內容,

image

最後修改的內容如下:

   1: @model ViewModelSample.ViewModels.CategoryDetailsViewModel
   2:  
   3: @{
   4:     ViewBag.Title = "Details";
   5:     Layout = "~/Views/Shared/_BootstrapLayout.cshtml";
   6: }
   7:  
   8: <h2>Category Details</h2>
   9: <div class="form well">
  10:     <fieldset>
  11:         <legend>Categories</legend>
  12:  
  13:         <div class="control-group">
  14:             <div class="display-label">
  15:                 <strong>@Html.DisplayNameFor(model => model.CategoryData.CategoryID)</strong>
  16:             </div>
  17:             <div class="display-field">
  18:                 @Html.DisplayFor(model => model.CategoryData.CategoryID)
  19:             </div>
  20:         </div>
  21:  
  22:         <div class="control-group">
  23:             <div class="display-label">
  24:                 <strong>@Html.DisplayNameFor(model => model.CategoryData.CategoryName)</strong>
  25:             </div>
  26:             <div class="display-field">
  27:                 @Html.DisplayFor(model => model.CategoryData.CategoryName)
  28:             </div>
  29:         </div>
  30:  
  31:         <div class="control-group">
  32:             <div class="display-label">
  33:                 <strong>@Html.DisplayNameFor(model => model.CategoryData.Description)</strong>
  34:             </div>
  35:             <div class="display-field">
  36:                 @Html.DisplayFor(model => model.CategoryData.Description)
  37:             </div>
  38:         </div>
  39:     </fieldset>
  40: </div>
  41: <div class="form-actions">
  42:     @Html.ActionLink("Edit", "Edit", new { id = Model.CategoryData.CategoryID }, new { @class = "btn" })
  43:     @Html.ActionLink("Back to List", "Index", null, new { @class = "btn" })
  44: </div>
  45:  
  46: <h3>Category's Products</h3>
  47: <table class="table table-striped table-bordered table-hover">
  48:     <tr style="background-color: #E8EEF4;">
  49:         <td>ProductID</td>
  50:         <td>ProductName</td>
  51:         <td>SupplierID</td>
  52:         <td>CategoryID</td>
  53:         <td>QuantityPerUnit</td>
  54:         <td>UnitPrice</td>
  55:         <td>UnitsInStock</td>
  56:         <td>UnitsOnOrder</td>
  57:         <td>ReorderLevel</td>
  58:     </tr>
  59:     @foreach (var item in Model.ProductCollection)
  60:     {
  61:         <tr>
  62:             <td>
  63:                 @Html.DisplayFor(modelItem => item.ProductID)
  64:             </td>
  65:             <td>
  66:                 @Html.DisplayFor(modelItem => item.ProductName)
  67:             </td>
  68:             <td>
  69:                 @Html.DisplayFor(modelItem => item.SupplierID)
  70:             </td>
  71:             <td>
  72:                 @Html.DisplayFor(modelItem => item.CategoryID)
  73:             </td>
  74:             <td>
  75:                 @Html.DisplayFor(modelItem => item.QuantityPerUnit)
  76:             </td>
  77:             <td>
  78:                 @Html.DisplayFor(modelItem => item.UnitPrice)
  79:             </td>
  80:             <td>
  81:                 @Html.DisplayFor(modelItem => item.UnitsInStock)
  82:             </td>
  83:             <td>
  84:                 @Html.DisplayFor(modelItem => item.UnitsOnOrder)
  85:             </td>
  86:             <td>
  87:                 @Html.DisplayFor(modelItem => item.ReorderLevel)
  88:             </td>
  89:         </tr>
  90:     }
  91: </table>

顯示畫面,

image

也許有人會覺得好像跟使用 ViewBag 的方式沒有什麼多大的變化,但可以看看 Category's Products 的 table,這邊的資料是直接從 View 的 Model 中所取得,而不是再去另外從  ViewBag 處理後取得。

使用ViewModel:Products 資料直接從 Model 中取得,因為是強型別,所以不用做轉型,

image

使用 ViewBag:之前的方式還需要從 ViewBag 取得資料後再做轉型,

image

 


不建議的 ViewModel 使用方式

相信有些人會在 Google 查詢 「ViewModel」,而查詢出來的第一筆的內容卻是讓我不以為然,他的作法就我所說的在 ViewModel 中含有「行為」,我在前面也有說過,在 View 的原始碼裡要避免對資料做直接的存取處理,因為 View 的責任在於顯示資料或是讓使用者輸入資料,不應該有任何的行為,否則使用 MVC 來開發網站就毫無意義了。

而有些人會說,我不會在 View 裡去做資料存取的處理,但在 ViewModel 加入 repository or service 是為了方便去判斷頁面上的資料顯示與否,例如:權限判斷,所以就必須在 ViewModel 加入 repository or service,這樣就可以在 View 裡去寫程式來做判斷,有時候為了貪圖方便或是取巧,往往會讓專案開發陷入不可預期的危機,尤其是多人開發的專案,你就會發現這樣取巧的做法,在有些開發人員的手中是會玩出讓人驚訝的花樣。

ASP.NET MVC 預設不會對 View 進行編譯,所以如果在 View 中加入了過多的程式邏輯,那麼在建置專案時並不會立即發現錯誤,往往要到執行後才會發現到問題與錯誤。

再來看看一段程式,

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Web;
   5: using BadSample.Models;
   6: using System.Web.Mvc;
   7: using BadSample.Controllers;
   8:  
   9: namespace BadSample.Areas.SystemOption.Models
  10: {
  11:     public class SomeThingRepository : BaseRepository
  12:     {
  13:         public IQueryable<SomeThing> FindAllSomeThing()
  14:         {
  15:             return db.SomeThing;
  16:         }
  17:  
  18:         public SomeThing GetSomeThing(Guid id)
  19:         {
  20:             return db.SomeThing.SingleOrDefault(x => x.SomeThingId == id);
  21:         }
  22:  
  23:         public void Add(SomeThing SomeThing)
  24:         {
  25:             db.SomeThing.InsertOnSubmit(SomeThing);
  26:         }
  27:  
  28:         public void Delete(SomeThing SomeThing)
  29:         {
  30:             db.SomeThing.DeleteOnSubmit(SomeThing);
  31:         }
  32:  
  33:     }
  34:  
  35:     public class SomeThingViewModel : BackendBaseViewModel
  36:     {
  37:         public SomeThingRepository SomeThingRepo = new SomeThingRepository();
  38:         public List<SomeThing> SomeThingList { get; set; }
  39:         public SomeThing SomeThingData { get; set; }
  40:         public List<SelectListItem> StatusList
  41:         {
  42:             get
  43:             {
  44:                 var itemlist = new List<SelectListItem>();
  45:                 itemlist.Add(new SelectListItem { Text = "上架", Value = "true" });
  46:                 itemlist.Add(new SelectListItem { Text = "下架", Value = "false" });
  47:  
  48:                 return itemlist;
  49:             }
  50:         }
  51:         public string Action { get; set; }
  52:         public SomeThingViewModel()
  53:         {
  54:             this.SomeThingData = new SomeThing();
  55:         }
  56:     }
  57: }

上面的這一個 SomeThingRepository.cs 檔案裡包含了「SomeThingRepository」與「SomeThingViewModel」兩個類別,然後 SomeThingViewModel 內有加入了型別 SomeThingRepository 的屬性,而且這個 ViewModel 還繼承了一個 BackendBaseViewModel……

一、不要為了偷懶就把好幾個類別都塞在同一個 cs 檔案中,尤其是 Repository 與 ViewModel 這兩種行為、責任截然不同的類別,這兩個根本就不應該放在一起,在日後做維護時會時常出現找不到正確類別在哪裡的問題,Respoitory 與 ViewModel 應該分開並且分別存放在不同目錄。

二、ViewModel 是因為 View 的需求而被建立,所以是依據 View 的需求而建立不同的屬性,ViewModel 不適合使用繼承。

三、ViewModel 內要使用 Property 而不要用 Field。

四、不要因為貪圖方便或是誤用「Reuseable」 的觀念而讓多個 View 共用一個 ViewModel,ViewModel 是因為 View 的需要而被建立,所以每個 View 都會有不同的資料需求,但實際的開發裡,真的會有很多頁面的 ViewModel 會有相同的情況,我的建議是不要共用!

假如有某一個頁面有修改而需要增加或不需要某個屬性後,那共用同一個 ViewModel 的諸多 View 就得跟著變動,加入新的屬性的問題是比較小,但是有些頁面並不需要新屬性,卻可以在編輯 View 時可以取用到加入的新屬性,會容易有誤用的情況,如果是 ViewModel 刪除屬性,那麼衍生的問題就會很大,這不用我說得太清楚,大家也知道會有什麼樣的後果吧!

五、ViewModel 不該有 Repository or Service 的屬性存在。ASP.NET MVC 所建立的 ViewModel 只是單純用來給 View 呈現資料,所以不需要也不應該加入行為(方法),尤其是會直接對 Model 內容的更改(CRUD)。

 

P.S.
之前看到有人覺得 ASP.NET MVC 使用 ViewModel 是一種不純正的方式!?我就真的不清楚所謂的不純正到底指的是什麼呢?

 


很多 ASP.NET MVC 的開發者會對 ViewModel 有很多的遐想甚至是「非分之想」,總是讓 ViewModel 去做他不應該做的事情,或是在他的身上加上很多不必要的東西,可能在編輯 View 時能夠有很多的取巧與「方便」,但是對於後續的維護與多人開發時往往會產生很多的問題,也因為 ViewModel 沒有很明確的規範與做法,所以各式各樣的實作方法就會層出不窮的出現,貼上 demo 對於 ViewModel 的說法,讓大家加深印象。

image

 

這一篇先簡單的說明建立 ViewModel 的建立與使用,至於 CRUD 的應用就另外再寫文章來說明。

 

延伸閱讀:

MSDN – ASP.NET MVC 3 Fundamentals > Exercise 5: Creating a View Model
http://msdn.microsoft.com/en-us/vs2010trainingcourse_aspnetmvc3fundamentals_topic7.aspx

Stephen Walther - ASP.NET MVC Tip #50 – Create View Models
http://stephenwalther.com/archive/2009/04/13/asp-net-mvc-tip-50-ndash-create-view-models.aspx

Rachel Appel on Software Development - Use ViewModels to manage data & organize code in ASP.NET MVC applications
http://www.rachelappel.com/use-viewmodels-to-manage-data-amp-organize-code-in-asp.net-mvc-applications

demoshop - ASP.NET MVC ViewModel不可遺忘的好幫手
http://demo.tc/Post/500

點部落 小朱的技術隨手寫 - [Architecture] MVP, MVC, MVVM, 傻傻分不清楚~
http://www.dotblogs.com.tw/regionbbs/archive/2011/09/29/compare.to.mvp.mvc.mvvm.aspx

 

教學影片

Introduction to ASP.NET MVC View Model

ASP.NET MVC Tutorial - Binding to a ViewModel List

MVC Music Store Part 3: Views and ViewModels 1/2

MVC Music Store Part 3: Views and ViewModels 2/2

twMVC Wade 所主講的「攻略 ASP.NET MVC 網站的從無到有 2012-06-14

 

以上

19 則留言:

  1. 好精闢的文獻!終於有深刻的了解!

    回覆刪除
  2. 非常抱歉,想請教步驟中control如何設定?
    因為我照做到step2,要做step3時,發現 那個"db"不知怎麼來的
    小弟算初學
    有去買書找範例,但範例是DataBase First,建立CONTROL時會建立資料內容類別
    然後會以該類別產生db,類似 private PurchaseItemModelEntities db = new PurchaseItemModelEntities();
    可是照上面的例子,我先產生兩個model,再產生ViewModel後,建立control時,資料內容類別不設為空白似乎過不了
    不曉得是什麼狀況?

    回覆刪除
    回覆
    1. 我這邊的例子都是以 EF 的 Database first 為主,其實不管哪一種策略產生方式,
      使用 VS 內建的新增 Controller 的方式都是一樣的,
      在「ASP.NET MVC 專案分層架構 Part.1 初學者的起手式(http://goo.gl/J6xA0)」這篇文章的「Controller 內容」有提到如何使用 Wizard 快速建立 Controller,
      請注意!
      我給你看的那篇文章,只需要看我提到的部份就好,其餘的內容以及相關系列文章請先跳過,因為你還是初學者,不適合看,看了只會讓你更加困擾。

      還有請注意,不是「control」而是「Controller」,因為講 control,在 ASP.NET 會直接聯想到「控制項」,

      最後,因為你是初學者,有關 ViewModel 的觀念,
      在你尚未對 ASP.NET MVC 的基本操作都還不熟悉的情況下,實在不適宜就直接冒然使用,
      ViewModel 的使用並不是一定要用,也不是每個 Controller 或是 View 都一定要去用,
      我會建議,請先試著用最基本的方式去用 ASP.NET MVC 來建立一個簡單的小應用,例如單一資料的新刪修查,
      等到這些都上手了之後再繼續,另外如果你對於物件導向程式設計不熟或是不懂,請先釐清基本的 OO 觀念。

      ViewModel 是因為 View 的需要而另外建立的,並非一開始就需要去建立出來,除非系統分析與系統設計有能力做到如此精細與精準的分析與設計,不然 ViewModel 都會因為是 ViewModel 的需要而產生的。

      如果你真的有很多問題的話,如果你是住在台北市或是新北市,那麼歡迎你在週四晚上到伯朗咖啡科大店來找我們,我們每週四都有固定聚會,此聚會為完全開放,不需要報名、沒有形式,帶著你的問題或是想法前來,我們會在 ASP.NET MVC 的開發上盡力協助你,相關詳細的聚會訊息可以查看 twMVC 官網 http://mvc.tw

      每星期四(PM 07:30 ~ 09:00)都可以在「伯朗咖啡科大館」看到我們(進門右轉到底)
      伯朗咖啡科大店 台北市大安區忠孝東路三段52號1樓 捷運忠孝新生站三號出口

      刪除
  3. IEnumerable ProductCollection 有多筆資料,若我想同時更新些資料 改如何達成

    回覆刪除
    回覆
    1. Hello,
      對於你的問題,我不是相當瞭解,因為你是在這一篇說明 ViewModel 的文章裡提問,所以我就以 ViewModel 的方式來回答,
      作法與沒有使用 ViewModel 的方式其實差不多,都是整批來整批送回去,其實我是認為你的提問並沒有那麼明確,
      我建議你將所遇到的問題以及部分程式碼或擷圖使用左邊的「詢問與建議」給我,
      我再依據你的問題來做回答(畢竟部落格的留言回覆無法提供比較明確的說明,而且也不適合做深度的問題討論)

      刪除
  4. 請問一下
    我是剛學MVC不久的初學者
    我想請問
    如果我想要一開始進入搜尋頁面的時候
    並沒有其他資料
    在我搜尋之後才出現我需要的那筆資料
    請問我應該怎麼做
    我在foreach那邊一直錯掉

    回覆刪除
    回覆
    1. Hello,
      既然沒有資料,那麼傳給 View 的 ViewModel 就應該會是 Null 或是給一個空的預設值內容
      如果是給 Null 的話,那就要在 View 的 foreach 前面加上是否為 Null 的判斷
      只要不為 Null 就會是有資料(或是你再加上判斷資料的數量),有資料才執行下面的 foreach 程式

      刪除
  5. Kevin 您好
    我這邊有一個使用 ViewModel 觀念的問題想請教您
    目前在 Controller 的地方透過 AutoMapper 轉換 Model 跟 ViewModel
    但在 Controller 更新的這個 action 有一些問題
    因為ViewModel 不會有 Model 全部的欄位 所以在轉換成 Model 時會在補回缺少的欄位資料(這邊 Repository Update(T model) 做全部更新)
    這樣子在 service 層就會都要做補資料的動作
    所以我想是不是在 service 層去告知要修改的欄位其他不更新
    不過這樣做 service 層就會有很多資料更新的方法
    目前有點困惑不知道改用哪種方式做比較好

    回覆刪除
    回覆
    1. 你好,相當抱歉,這一年來都相當忙碌,忙到文章也沒寫幾篇,留言也沒有時間立即回覆,
      其實有點無法明白你的需求。
      如果是以更新來說,更新資料時,當網頁上把更新表單的資料 POST 回後端,
      後端會先取出原本的物件,然後去判斷哪一個屬性有被更新,然後將要更新的屬性資料換成新的,
      使用 Automapper 的處理,其實可以在 Mapper.CreateMap 的時候就可以在各個屬性去個別指定置換的處理,
      這麼一來就可以把原本的物件資料給置換成新的,然後就可以再丟給後面的 Service 與 Repository 去做後續的處理。
      應該是這樣,休假日再來試試看。

      刪除
    2. 謝謝您的回覆,
      我在思考看看
      我匯在找個時間到 twMcv 跟大家討教一下
      :)

      刪除
  6. 您好
    [DisplayName("姓名")]
    public string Name { get; set; }
    因為欄位需要描述所以寫在viewmodel裡可以吧

    [DisplayName("姓名")]
    [Required(ErrorMessage = "請輸入姓名")]
    public string Name { get; set; }
    如果加入了驗正的話是否就應該寫在model裡呢

    謝謝

    回覆刪除
    回覆
    1. Hello, 你好
      其實寫在 ViewModel 也可以,應該說,如果你使用 ViewModel 讓資料在 View 與 Controller 做傳遞,
      那麼就是將驗證的屬性加在 ViewModel 上面,
      如果你是使用原本的 Model 型別在 View 與 Controller 做傳遞,就是放在原本的 Model 裡,
      主要是看你用哪一個型別在做資料的傳遞,因為在做驗證的時候是要對傳遞的型別去做驗證,
      如果傳遞的型別是用 ViewModel 但是資料驗證的屬性卻是放在原本的 Model 型別裡,
      那麼驗證是起不了作用的.

      刪除
  7. 我新接觸MVC 5.
    有點不太明白兩點。
    1. ViewModel 不該有 Repository or Service 的屬性存在
    repo和service是指什麼?
    2.(View 不應該有任何的行為)
    意識是指Math.Round,或者做if else都不應該?

    這是一篇激好的文章!
    就如大大所說,我現在接手的專案就是大量ViewBag再在View中做foreach >_<

    已subscript了大大的RSS!我們都愛MVC ^^

    回覆刪除
    回覆
    1. Hello,因為你是 ASP.NET MVC 的新手,所以我不應該跟你說太多,請見諒。
      這是為了不讓你的觀念有所混淆,但還是稍微說明一下,
      Repository 與 Service 都是類別,但是這兩種類別的行為(做的事情)都不太一樣,
      Repository 是指資料倉儲模式,將有關對於資料的存取修改等操作集中在一起,放在個別不同的 Repository 裡,
      Service 服務類別,集中放置系統商業邏輯的部分,不牽涉到怎麼存取資料的處理,只關注資料怎麼用,

      行為,剛剛有說過,就是類別裡面所定義的方法,這些方法都是用來操作資料或是狀態,所以就是行為,
      ViewModel 只是一個單純的資料容器類別,所以不應該有行為在裡面,
      這是為了要能夠職責有所區分,而不會讓資料處理的程式給四散在各地。

      先講到這裡,在你往後的開發過程中再去慢慢體會,你就可以瞭解。

      刪除
  8. Respoitory 與 ViewModel 應該分開並且分別存放在不同目錄
    => Repository

    回覆刪除
  9. 您好請問一下
    ASP.NET MVC 使用 CKEditor.Mvc 與 HtmlEncodeOutput 補充說明 http://kevintsengtw.blogspot.tw/2013/11/aspnet-mvc-ckeditormvc-htmlencodeoutput.html
    文中step.7的ArticleViewModel
    也是ViewModel嗎?

    回覆刪除
    回覆
    1. 是的,我的程式都有依循一定的規則,都會以後綴字去區別
      以 ArticlViewModel 的後綴看來就是 ViewModel

      刪除
  10. 推~ 以前第一次看沒深刻理解,現在回頭過來再看一次,覺得好清楚。

    回覆刪除

提醒

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

最近的留言