當時主管跟我說:「這種類型的欄位會常改,所以取資料用DataTable,不要用 Model Binding」
我:「好,我會維持既有程式碼風格」
但我心裡卻在想——不管怎樣,最後這個魔法字串還不是要改?
DataTable dt = _someQuery.GetInfo(x);
// 檢查限制條件 狀態要是WAIT 才可以進行變更流程
if (dt.Rows[0]["STATUS_CODE"].ToString().ToUpper().Trim() != "WAITING")
{
throw new Exception("STATUS_CODE SHOULD BE WAITING");
}
放上一張同源問題的梗圖,以上只是冰山一角,那時的專案特徵:
❌ 沒有 Model Binding(全靠 DataTable
靈魂對話)
❌ 欄位命名模糊到需要「靈感」或「通靈」才能猜出用途
免定義 Model → 想查什麼欄位直接塞進去
動態欄位支援 → 適合欄位不固定的資料(CSV / Excel)
跟舊系統相容性好 → 很多老 API、報表元件只吃 DataTable
一次裝多筆資料 → 記憶體中快速過濾、分組
不過放到 2025 年的新專案,它的優點很容易變成技術債,因為會:
依賴魔法字串
缺乏型別安全
效能輸給 Model ORM(像 Dapper)
錯誤示例:
if (dt.Rows[0]["STATUS_CODE"].ToString() == "WAITING") { ... }
"WAITING"
、"STATUS_CODE"
全是 Hard Coding,散落四海八荒,改一次要全專案搜尋,還要祈禱大小寫、空白沒暗殺你。
你不加 Enum,我只能去擲筊問神佛,才知道你在定義什麼。
修正版:
public enum StatusCode
{
[Display(Name = "等待", Description = "產線運送中")]
Waiting,
[Display(Name = "作業中", Description = "清洗中")]
Processing,
[Display(Name = "完成", Description = "已完成清洗")]
Completed
}
if (order.StatusCode == StatusCode.Waiting) { ... }
型別安全、你IDE可以輔助你、改名不用全專案在那邊搜。
錯誤示例:
if (dt.Rows[0]["STATUS_CODE"].ToString() != "WAITING") { ... }
dt資料為空?恭喜觸發 IndexOutOfRangeException
,例外訊息簡潔到令人感動(反諷),前端只能回:
「欸後端,爆了,你自己看…我只是按了查詢而已。」
修正版:
if (!dt.Any())
throw new NotFoundException("Order not found");
真要這樣寫,也要先檢查資料是否存在,並用有意義的例外類別,讓前端知道是「資料不存在」而不是「系統壞了」。
錯誤示例:
(int)row["ORDER_QTY"]
某個同事可能把欄位型別改成 decimal
,上線直接 InvalidCastException
,沒有任何編譯期警告。
修正版:
public class Order
{
public decimal OrderQty { get; set; }
}
var order = conn.QueryFirstOrDefault<Order>(...);
用 Model + Dapper,欄位型別錯誤直接在編譯期爆。
錯誤示例:
public void UpdateOrder(string id)
{
// 原始程式碼有500行吧,有部分邏輯甚至寫在預存程式,給我整無語
// 查詢 + 驗證 + 寫入 + 發通知 全黏在一起
}
要測試某段邏輯得 mock 半個世界,還要假裝有 DB 和 SMTP Server,我寫測試比寫邏輯還麻煩是怎= =
修正版:
public bool CanUpdateOrder(Order order) { ... } // 純邏輯,可單測
public void UpdateOrderInDb(Order order) { ... }
public void NotifyOrderUpdated(Order order) { ... }
拆成可單獨測試的邏輯方法,減少外部依賴。
錯誤示例:
throw new Exception("STATUS_CODE SHOULD BE WAITING");
前端完全不知道這是查無資料、狀態錯誤,還是 DBA 在搞破壞。
修正版:
throw new BusinessRuleException(
"Order status must be WAITING",
ErrorCodes.StatusNotWaiting
);
自訂例外類型 + 錯誤碼,前端可依錯誤碼判斷行為並顯示友善訊息。
DataTable 不是原罪,在舊系統或特定場景它依然好用,但新專案長期維護時,缺點會被無限放大。
能動 ≠ 寫得好。
在多人協作的專案裡,型別安全、可測試、可維護,比事後救火更省命。