如果你剛開始接觸 .NET,並開始閱讀有關 Web API 的技術手冊或書籍,你很可能會遇到儲存庫模式。乍一看,這似乎是透過添加額外的層級使事情變得複雜,但實際上,這是一種防止程式碼變得混亂、難以長期維護的策略。
簡而言之,我們是在應用程式的領域和資料庫之間添加一個中間層。這樣,控制器就不需要知道如何編寫 SQL 查詢語句或了解 Entity Framework 的工作原理,而只需向一個知道如何獲取數據的對象請求數據,而無需考慮該對象使用的內部方法。
這種圖案究竟由什麼構成?
把儲存庫想像成一個黑盒子。你透過用戶的 ID 請求該用戶,它就會回傳給你。使用者資料來自 MySQL 資料庫、JSON 文件,甚至是外部 API 都無關緊要。這種資料層的抽象化使得你的應用程式核心可以與具體的儲存方式無關。
在 C# 生態系統中,這是透過實作介面來實現的。我們在介面中定義要執行的操作(例如插入、刪除或搜尋),然後建立一個類別來實作該介面並包含實際的 Entity Framework 邏輯。這樣,系統的其他部分只了解接口,而不需要了解具體的實現。
實施它的充分理由
儘管有些人可能認為這是一個過時的概念,但它仍然至關重要,原因有幾個。首先,它便於維護。如果您被迫更改資料庫(這種情況並不常見,但並非不可能),您無需重寫所有業務邏輯,只需重寫儲存庫類別即可。
另一個關鍵點是單元測試。如果你的業務邏輯與資料庫上下文 (DBContext) 緊密相關,那麼測試起來會非常麻煩,因為你需要一個真實可用的資料庫。透過使用程式碼倉庫,你可以建立一個模擬物件或模擬器,在記憶體中返回虛擬數據,從而在幾毫秒內驗證你的程式碼,而無需實際操作資料庫。
關於實體框架的爭論
許多人疑惑,既然我們已經在使用DbContext 和 DbSet,這種模式是否真的必要,因為 Entity Framework 從技術上講已經實現了一種工作單元和儲存庫。答案是,這取決於你的需求,但添加額外的層有助於將程式碼與框架解耦。
如果你的專案只是一個簡單的 CRUD 應用,這可能是不必要的開銷。然而,在需要清晰架構的企業級專案中(例如六角形架構或洋蔥架構),將領域程式碼與基礎架構分開是保證應用可擴展性和靈活性的唯一途徑。
通用儲存庫和工作單元
為了避免為每個實體(使用者、產品、訂單等)編寫相同的插入和刪除程式碼,通常會使用IGenericRepository<T>。它使用泛型類型為任何類別提供基本操作,從而大幅減少程式碼冗餘。
這就是工作單元(Unit of Work)發揮作用的地方。當多個程式碼庫同時運行時,每個程式碼庫都可能單獨打開資料庫連接,從而存在風險。工作單元可以集中管理這些連接,確保所有程式碼庫共享相同的上下文。這對於事務管理至關重要:要么所有程式碼庫的更改都被保存,要么在發生錯誤時不保存任何更改。
在開發流程中的實際應用
為了實現這一點,我們首先定義包含必要方法的介面。然後,儲存庫類別注入DBContext來執行查詢。最後,在控制器中,我們不直接注入上下文,而是注入儲存庫介面或工作單元。
這樣做可以大大減輕控制器的重量。無需再進行管理。 複雜的過濾器和連接 在 SQL 中,您只需呼叫類似這樣的方法即可。 GetById如果我們需要有效率地過濾數據,我們可以將 lambda 表達式傳遞給通用儲存庫,以便資料庫完成繁重的工作,而不是 Web 伺服器,從而避免處理大量資料時出現效能問題。
這種方法可以保護應用程式的核心免受外部變更的影響,使開發人員能夠專注於業務規則。透過依賴注入實現組件解耦,我們建立了一個系統,其中每個組件都是獨立的、易於替換的,最重要的是,能夠更好地抵禦常見的持久化錯誤。請分享此信息,以便更多用戶了解它。
