[筆記] ASP.NET MVC5 - Ch.2 Model
MVC 中,演算法由 Controller 提供,而 data 由 Model 提供
Model 中的 data 來自資料庫、 檔案、Web Service、其他應用程式或系統,或不同程式所演算出來的結果,都算是 Model 的範圍
Model 通常會放在專案的 Models 資料夾內,是基於 ASP.NET MVC 所強調「習慣替代配置」原則,這個原則適合小型應用程式,若一開始就知道要開發大型應用程式 Models 資料夾就不適合放系統層次的 Model 物件,而只能放針對應用程式本身的 Model,甚至不放都可以
Model 的類型
Section titled “Model 的類型”Dino 的分類
Section titled “Dino 的分類”Dino Esposito 將 ASP.NET MVC 中的 model 分為三種:Domain Model、View Model、Input Model
- Domain model 的結構相應於來自資料庫的 data
- View model 的結構相應於畫面呈現
- Input model 則是由使用者端或外部系統端輸入的 model <— 這不是包含在 view model 內?
Input model 會和 MVC 的 model binding 機制協同合作,以提供簡便的資料輸入繫結方式
領域驅動設計的分類
Section titled “領域驅動設計的分類”以領域驅動設計(Domain-Driven Design, DDD)為基礎,將 model 分為三種:
- Entity:具有明確辨識能力的 Entities
- Value Object:可與其他 Entity 所共用的資料物件,稱為 value object
- Service:供應 entity 或 value object 所需資料的程式或動作,稱為 service
Model 的設計
Section titled “Model 的設計”網路上的一種說法:
Model 要肥,Controller 要輕 Fast model, skinny controller
意思是指,model 要顧全整個應用程式需要的資料,controller 必須輕量化以縮短反應時間
一般而言,應用程式需要什麼資料,model 就怎麼設計,這是小型應用程式常見的作法,也就是說,依據畫面決定 model 的結構
然而對中大型應用程式來說,重點會放在如何解決特殊領域的問題,完成特定領域的工作或滿足特定領域的需求,也就是「商業邏輯」的部分
DTO:Model 的一種設計方式
Section titled “DTO:Model 的一種設計方式”Model 的其中一種常見的設計方式,叫做 DTO(Data Transfer Object, 資料傳輸物件)
DTO 是只有屬性成員(Property-Only)的 class 物件,只有預設建構式(constructor)及屬性存取子(property getter),沒有方法(method)及其他成員
另一種設計方式:POCO
Section titled “另一種設計方式:POCO”還有另一種常見的設計方式,叫做 POCO(Plain-Old CLR Object)
跟 DTO 不同的是,POCO 可擁有 method,已針對資料進行驗證,並可保存物件當下的狀態
DTO 與 POCO 的比較
Section titled “DTO 與 POCO 的比較”| DTO | POCO |
|---|---|
| 只有屬性 | 有屬性及方法 |
| 負責裝載資料,無法存狀態 | 可保存當下狀態 |
Model 的中介層: repository
Section titled “Model 的中介層: repository”- model 利用中介層 repository 與資料來源(例如:database)溝通
- 中介層規定資料來源怎麼處理 model,包括資料存取、資料轉換等
- 可進一步使用 interface 及抽象工廠模式(Abstract Factory Pattern)設計具抽換能力(pluggable)的 repository,以提升 model 與不同資料來源整合的相容性
跟 TypeScript 一樣,ASP.NET 也有泛型(其實應該反過來講才是,因為 TypeScript 是以 C# 為基礎發展出來的)
- 泛型於 .NET Framework 2.0 中首次加入
- .NET Framework 的泛型實作都在
System.Collections.Generic的 namespace 裡 - 常用的幾個類別集合都支援泛型,例如:
List、Dictionary、Hashtable、Stack、Queue
泛型雖然是動態的,依當下傳入的型別而定,但有時候,我們希望能夠限制傳入的型別,C# 裡使用 where 這個字,限制傳入的型別。
例如:
GenericTypeName<T> where T: constraint1, constraint2constraint1 與 constrain2 即是我們要約束 T 的型別
型別約束的種類
Section titled “型別約束的種類”型別約束的種類大致分為六種:
約束為 struct
Section titled “約束為 struct”where T: struct
- 型別參數必須是實質型別
- 可以指定 nullable 以外的任何實質型別
static void Swap<T>(ref T input1, ref T input2) where T : struct { }約束為 class
Section titled “約束為 class”- 型別參數必須是參考型別,其中包含:任何的 class、interface、delegate 或 array 型別
static void Swap<T>(ref T input1, ref T input2) where T : class { }約束為 new()
Section titled “約束為 new()”- 型別參數必須擁有宣告為 public 的無參數建構式
- 將 new() 條件約束與其他條件約束一起使用時,一定要將其指定為最後一個
static void Swap<T>(ref T input1, ref T input2) where T : new() { }約束為 <base class name>
Section titled “約束為 <base class name>”- 型別參數必須本身式指定的 base class,或繼承自該 class
static void Swap<T>(ref T input1, ref T input2) where T : BaseEmployee { }約束為 <interface name>
Section titled “約束為 <interface name>”- 型別參數本身式指定的 interface,或實作該 interface
- 可以同時指定多個 interface 約束
- 限制的 interface 也可以是泛型
static void Swap<T>(ref T input1, ref T input2) where T : IEmployee { }約束為 U(另一個泛型)
Section titled “約束為 U(另一個泛型)”- T 必須繼承自 U
static void Swap<T, U>(ref T input1, ref U input2) where T : U { }參考資料: C# Generic Methods - Introduction, Constraints, Examples
大概先知道有哪些泛型約束的種類,要使用到的時候再來深入研究
Model 的實作
Section titled “Model 的實作”- 通常會以 database 的結構來設計
- 以 DTO 來設計,只有屬性,沒有方法
- 要注意 model 各欄位的型別是否與 database 相容
- SQL 指令一定要用參數化查詢法(Parameterized Statement/Query),避免 SQL injection 的漏洞