前情提要
筆者工作環境中開發ASP.NET CORE Web API
,資料庫存取部份,會交叉使用EF Core
以及Dapper
,筆者單位共同認知的切分方式為若單個資料表的CRUD操作,就使用EF Core
,若複雜的查詢,或者呼叫Store Procedure等會使用Dapper完成操作。單個資料表的CRUD
,基本上透過EF Core
可以說是輕而易舉,會遇到一個問題是資料表共同欄位,類似於CreatedDate, CreatedBy, UpdatedDate, UpdatedBy
這種紀錄變更者及變更時間部份,若寫在每一個Service
中會顯得有點多餘,且沒有效率,擴充性也不好,假設現在要改變CreatedDate, UpdatedDate
的時間原本由Local Time
改為UTC Time
,其不就是要在每個Service中做修正。
內容
鑒於此,應該要想個辦法找出共同方法中做統一處理,筆者知道EF Core
的SaveChanges()
方法是可以覆寫的,也就是說我們只要在SaveChanges()
方法中判斷新增或更新,針對不同欄位做更新值的動作,等於完成幫所有資料表的共同欄位做更新。
筆者工作環境中遇到的情境通常都是資料庫端已設計好資料結構,再撰寫程式的居多,這樣一來EF Core
部份會選擇使用scaffold
的方式自動產生資料庫對應的物件類別。筆者會在另一篇獨立講解scaffold
的使用方式,這篇就先跳過,再來因資料表對應物件類別為工具產生出來的,盡量不要手動改寫,這時候要托Partial Class
之福,我們可以設計Partial Class
做覆寫作業。
設計共同欄位的介面
1 | public interface IChangesEntity |
製作共同欄位介面
主要是將把共同欄位做介面的宣告,這個是一個通用的技巧,若宣告介面,你寫泛型T
相關Method
或者類別
都有幫助,因為那個泛型T
限制為要實作某一個介面,好處就是在程式碼中可以點出相關Property
。我們就沿用這個概念於此,就把共同欄位抽出來變成一個獨立的介面:
1 | // 共同欄位-主要識別碼 |
宣告各類別的Partial Class
基本上Entities類別是由scaffold自動產出,我們為了各類別有實作上步驟所制定的介面,我們必須自己宣告一個Partial Class,實質意義只是為了宣告實作介面而已。
1 | // 這邊namespace要特別注意,須與相對應的實際Entity類別同一個namespace,才有Partial效果 |
DBContext覆寫SaveChanges()
基本上API部份有做Authorize,托.net core DI
設計之福,很容易取得登入者,只要注入IHttpContextAccessor
,可以輕鬆取得該操作者(登入者)資訊。再來就是依照Entry的狀態,可以針對相對應的欄位做編輯。
1 | public partial class XXContext |
宣告完這些步驟後,使用端完全沒有感覺到任何的改變,只要操作DbContext
下相對應的Entity物件,只要做SaveChanges()
,就會執行base的SaveChanges()
之前把那些共同欄位值做更新作業,達成擴充性高的一種實作方式。
結論
這篇主要利用Partial Class
及Override
的技巧,達到其統一由一套Method將共同欄位值更新作業,擴充性高,筆者也在上面文中舉過例子,編輯時間的取值調動,我們可以再想一個情境,編輯者目前是透過IHttpContextAccessor
注入的方式取得,若要改成其他方式取得編輯者,若設定編輯者的程式碼散落在各個地方是,不可能確保沒有漏網之魚,且花費成本顯然比改DbContext.Partial.cs
來得高很多,因此筆者認為此文中的解法已經算上上之選了。
參考