Skip to content

淺談 Entity Framework 中的預設值行為

TLDR

  • 當 SQL Server 欄位設有預設值時,若 Entity 屬性值與該型別的 C# 預設值一致,EF Core 在執行 INSERT 時可能會忽略該欄位,導致資料庫觸發預設值。
  • 建議盡量避免依賴 SQL 預設值,若必須使用,請確保 SQL 預設值與 C# 預設值一致。
  • 針對字串型別,建議統一設為 NOT NULL Default '' 以處理空值。
  • 更新資料時,若屬性值未變更,SaveChanges() 會回傳 0,判斷更新成功與否應考慮 EntityState.Unchanged 的情況。
  • 使用 AsNoTracking() 查詢後,若手動將 EntityState 設為 Modified,會導致該 Entity 的所有欄位在 UPDATE 時被強制更新,而非僅更新異動欄位。

TIP

本篇的完整可執行範例:CloudyWing/EfCoreBehaviorSample

Entity Framework 的預設值寫入行為

什麼情況下會遇到這個問題:當資料庫欄位定義了預設值(Default Value),且開發者在程式中未明確賦值或賦予的值與 C# 預設值相同時。

測試結果顯示,Entity Framework 並非根據「是否設值」來判定異動,而是透過比對「新舊值」來決定是否產生 INSERT 語法。當屬性值與該型別的預設值(如 null0)一致時,EF Core 可能會忽略該欄位,改由資料庫處理預設值。

若使用 EF Core Power Tools 反向工程,針對 bit 等型別產生 bool? 並標記 Required 時,EF Core 可能會產生 DEFAULT VALUES 語法,進而忽略所有欄位。

建議做法

  • 盡量避免在資料庫層級依賴預設值。
  • 若必須使用,請確保 SQL 預設值與 C# 程式碼中的預設值邏輯一致。
  • 字串型別建議統一設為 NOT NULL Default '',確保 null 與空字串處理邏輯一致。

更新 Entity 時的狀態判定

什麼情況下會遇到這個問題:在執行 Update 操作時,若 Entity 的屬性值與資料庫現有值完全相同,SaveChanges() 會回傳 0

當 Entity 屬性值未變更時,EntityState 會保持為 Unchanged,此時不會產生任何 UPDATE 語法。

建議做法: 在 Business Service 的更新邏輯中,應使用以下方式判斷執行結果,避免誤判:

csharp
bool isSuccess = context.Entry(entity).State == EntityState.Unchanged || context.SaveChanges() > 0;

AsNoTracking 與 EntityState 的副作用

什麼情況下會遇到這個問題:在查詢時使用了 AsNoTracking(),隨後為了更新資料而手動將 EntityState 設為 Modified

當使用 AsNoTracking() 查詢後,Entity 不會被 Change Tracker 追蹤。若手動將其狀態設為 Modified,EF Core 將無法得知哪些欄位真正發生了變更,導致 UPDATE 語法會更新該資料表的所有欄位,而非僅更新異動欄位。

建議做法

  • 若非必要,請勿濫用 AsNoTracking()
  • 若需更新資料,應使用標準查詢方式讓 EF Core 追蹤變更,這樣產生的 UPDATE 語法只會包含已變更的欄位,效能較佳。

異動歷程

    • 初版文件建立。
    • 補上對應 GitHub 範例專案連結。