前情提要
筆者工作環境中開發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來得高很多,因此筆者認為此文中的解法已經算上上之選了。
參考