先前我們已經用 Scaffolding 建立簡易的 CRUD 功能,不過可以發現,像是 Create 時,理應不需要填寫 CreateUser, CreateTime, UpdateUser, UpdateTime 的欄位,這時候我們該怎麼辦才好呢? ViewModel 就可以派上用場了
ViewModel 就像是針對不同的 View 所建立的 Model,它可以用來與 Controller 及 View 之間傳值,並且是強型別的方式,避免使用 ViewData、ViewBag 弱型別會導致型別錯誤的問題發生。
且 ViewModel 也能協助我們將兩個不同 Model 結合起來,e.g. Events 和 EventsInfo,這在後續會實際撰寫到。
在前言提到,CreateUser, CreateTime, UpdateUser, UpdateTime 的欄位我們是不需要自行填寫的,且 UpdateUser, UpdateTime 更是不會在新增頁面就儲存這兩個欄位的值到 DB,所以我們在 ViewModel 中就不會放 UpdateUser, UpdateTime。
另外有個 CategoryId 的欄位,先前我們有設計了一個 EventsCategory 的 Model 與 Table,不過現在我們先為了方便測試,就先使用 enum 的類別來建立下拉選單與傳值給 CategoryId。

其實就只要把原本的 Model 複製過來,大部分就完成了,只是可以看到我多新增了 EventsCategoryEnum 這一個 Field。

在 View 的第一行開始通常會是指定這一個 View 要接收哪一個 Model,在原本 Events 的 Create.cshtml,Model 是指定為 Events,此時我們要更改為 EventsCreateViewModel。

另外也可以看到參考了 EventsSystem_iThome.Enum,因為下拉選單會參考 EventsCategoryEnum 類別,並使用 Tag Helper 做出下拉選單。
Model 指定為 EventsCreateViewModel 後,可以發現 UpdateUser, UpdateTime 的部分會發生錯誤,這是因為 EventsCreateViewModel 內沒有這兩個欄位,所以無法指定。
而我將 CategoryId 的 Code 也移除了,替換的是 Enum 的取值:

Scaffold 的 Controller,在 Model Binding 預設是產生弱型別的 Binding(Create()的參數),這樣的方法其實是不好的,且如果欄位增減,參數也需手動更新。
ASP.NET Core 提供了 Model Binding 的功能,Action 的參數只要指定好(e.g. 型別指定為 Model;或是 int Id,參數名設定跟 PK 一樣),那麼在 View 傳值到後端時,ASP.NET Core 會自動幫你將傳過來的值綁定在參數。
不過要傳到 DbContext 並新增資料至 DB 時,DbContext 接收的是原本的 Events Model 類別,EventsCreateViewModel 是無法直接放在 DbContext 的,此時需要將 ViewModel 的值都丟還給 Model。
可以 new Events 並將 EventsCreateViewModel 的 field 都一個一個指派到 Events,但這樣實在太慢了,此時有一個非常棒的套件可以來協助我們,AutoMapper。
使用 Nuget 安裝 AutoMapper。
AutoMapper 讓我們可以用幾行的 Code 就做到 ViewModel 與 Model 間的欄位傳值,首先建立一個對應的型別定義 MapperConfiguration,在 <> 的左方為來源型別,右方則為目的型別,左方會放置 ViewModel,右方則是 Model。
接著實體化對應器 mapperConfig.CreateMapper();,實體化後使用 Map() 並先指定目的型別(Events 這一個 Model),並將 EventsCreateViewModel 放在參數中,如此就完成了 ViewModel 與 Model 間的對應,現在 Model 的 Field 已經都存好值了。
此時將對應完的 events 放到 DbContext,就可以執行新增的 Function 了!
ViewModel 提供了更輕量化且更有彈性的 View 的傳值中介,ViewModel 與 View 一樣,它不該放置任何邏輯與其他方法,越單純越好。
AutoMapper 為 Model 間的對應減少了非常多的 Code,不需要寫很多個 A = B 就可以完成對應,我認為是一個在使用 ASP.NET Core 開發程式時,必須要安裝的套件。
[鐵人賽Day11] ASP.Net Core MVC 進化之路 - View(1) / 資料傳遞及Razor語法
ASP.NET MVC 的 ViewModel - 基礎篇
列舉類型 (c # 參考)
Get Int Value From Enum in C#