| 模式 | 起點 | 產出 |
|---|---|---|
| Database First | 既有資料庫 | 程式碼 (EDMX → TT 模板) |
| Model First | 視覺化設計師 (EDMX) | 資料庫 + 程式碼 |
| Code First (本教學) | POCO 類別 | 資料庫 (自動建立/遷移) |
Code First 最大優勢:領域模型純淨 (POCO),無基底類別綁定,易測試、易序列化 (JSON/XML)。
// Product.cs
public class Product
{
[ScaffoldColumn(false)] public int Id { get; set; }
[Required] public string Name { get; set; }
public decimal Price { get; set; }
public decimal ActualCost { get; set; }
}
// Order.cs
public class Order
{
public int Id { get; set; }
[Required] public string Customer { get; set; }
public virtual ICollection<OrderDetail> OrderDetails { get; set; } // 導航屬性
}
// OrderDetail.cs
public class OrderDetail
{
public int Id { get; set; }
public int Quantity { get; set; }
public int OrderId { get; set; } // 外鍵
public int ProductId { get; set; } // 外鍵
public virtual Product Product { get; set; } // 導航屬性
public virtual Order Order { get; set; }
}
Id 慣例對應 Identity 主鍵,資料庫自動產生。[ScaffoldColumn(false)]:MVC 編輯樣板隱藏此欄位。[Required]:模型驗證必填。virtual:啟用 EF 延遲載入。OrderId、ProductId 自動對應關聯。Order (1) ─────< (多) OrderDetail (多) ─────> (1) Product │ ▲ └────────────── 導航屬性 ───────────────┘
EF 會自動在資料庫建立 Foreign Key Constraints。
EF 使用反射掃描模型屬性,**必須先建置專案**產生組件,否則「新增控制器」對話框的模型類別下拉會是空的。
Order.OrderDetails → OrderDetail.Order 形成雙向循環,預設 JSON 序列化會無限遞迴拋出例外。修改 App_Start/WebApiConfig.cs:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}", new { id = RouteParameter.Optional });
// 關鍵:保留物件參考 ($id/$ref)
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
// 僅需 JSON,移除 XML Formatter 減少管線開銷
config.Formatters.Remove(config.Formatters.XmlFormatter);
}
}
序列化結果範例:
[
{ "$id": "1", "Id": 1, "Customer": "Bob", "OrderDetails": [
{ "$id": "2", "Quantity": 2, "Order": { "$ref": "1" }, "Product": { "$id": "3", "Name": "Tomato Soup" } }
]}
]
$id 標記物件身分,$ref 參考既有物件,完美解決循環。
| 方案 | 優點 | 缺點 |
|---|---|---|
| PreserveReferencesHandling.Objects (本文) | 零代碼修改模型、完整保留圖結構 | JSON 體積稍大、非標準 JSON (需 Newtonsoft 解析) |
| DTO/ViewModel 扁平化 | 標準 JSON、前端友善、解耦領域模型 | 需手寫映射代碼 (可用 AutoMapper) |
| [JsonIgnore] 忽略反向導航 | 簡單 | 失去反向導航資訊、序列化不完整 |
建議:內部 API 用 PreserveReferences;對外公開 API 必須用 DTO 扁平化,確保穩定契約。