前面已經介紹過Entity Framework的工作單元和映射層超類型的封裝,從本文開始,將逐步介紹倉儲以及對查詢的擴展支持。
什么是倉儲
倉儲表示聚合的集合。
倉儲所表現出來的集合外觀,僅僅是一種模擬,除了測試以外,沒有理由使用內存中真正的集合來創建倉儲。
不應該為所有實體建立倉儲,只有聚合才擁有倉儲。
倉儲用來重建已持久化的聚合,而工廠用於新建聚合。
使用倉儲的優點
直接使用Entity Framework的DbContext不是很好嗎,為什么還要在DbContext的上方封裝一層倉儲呢,這是否多此一舉?
很多使用EF的程序員確實是直接使用DbContext,並且他們發現開發起來十分簡單,因為DbContext的接口設計得非常優雅,從接口上看,DbContext就好像所有實體集合的倉庫。
另一方面,很多使用了倉儲的朋友,都是依葫蘆畫瓢,雖然創建了倉儲,但並沒有體會到多大好處。
下面簡要介紹使用倉儲將為你帶來的優點。
從概念上簡化數據操作
倉儲模擬了某種聚合的集合,而DbContext包含了N種類型的集合。
與倉儲相近的一個數據訪問模式是數據訪問對象(DAO)模式,很多人認為倉儲不過是數據訪問對象換了一個名詞而已,從技術上說的確如此,倉儲的強大之處在於概念上更簡單。倉儲在概念上代表內存中的集合操作,而數據訪問對象代表數據庫操作,很明顯,集合比數據庫在概念上更簡單。
減少冗余查詢邏輯
如果直接使用DbContext,由於特定查詢邏輯沒有一個固定位置,可能分散到任何地方,這很容易造成冗余。
倉儲是封裝特定查詢邏輯的好地方,對於特定的查詢邏輯,放到與聚合相應的倉儲中即可。
降低耦合度
直接使用DbContext,所有調用代碼與EF實現高度耦合。
另一方面,由於DbContext能夠獲取任意實體,這些實體可能位於聚合內部,這樣會破壞聚合的封裝性,同時在任意位置可以獲取任意對象,由於缺乏約束力而導致更高的耦合。
方便單元測試
直接使用DbContext只能進行集成測試,必須連接到真實數據庫。而領域層只持有倉儲接口,所以測試時很容易替換成模擬實現,從而避開數據庫。
使用倉儲的要點
使用更具體的倉儲
一般來講,ICustomerRepository比直接使用泛型的IRepository<Customer>要好。
為了定義通用操作,我們會創建一些泛型基類來實現基礎操作。
有些人發現很多具體倉儲只是直接繼承泛型倉儲,比如ICustomerRepository和CustomerRepository從泛型的IRepository<Customer>和Repository<Customer>派生,但CustomerRepository本身並沒有其它什么代碼。於是很多人學會偷懶,直接使用泛型基類進行操作。
ICustomerRepository優於IRepository<Customer>的原因如下。
1. 概念上更清晰。
ICustomerRepository從概念上良好表達了該倉儲用於操作客戶,而泛型倉儲可以操作任何聚合。
2. 為特定查詢邏輯提供唯一封裝和訪問點。
ICustomerRepository可以將客戶相關的各種復雜查詢封裝起來,而IRepository<Customer>很難對客戶查詢進行封裝,一個辦法是使用擴展方法來完成封裝,不過比起CustomerRepository要麻煩得多,而且實現代碼也更難管理。
3. 降低耦合。
泛型IRepository<>可以在任意位置獲取任意聚合,這比起DbContext要強一些,不會破壞聚合的封裝性,但缺乏約束力仍然會導致更高耦合。
ICustomerRepository通過明確的聲明,只能獲取需要的聚合,從而控制了對象的訪問。
倉儲僅返回與其聚合高度相關的內容
倉儲可以返回對應的聚合,也可以返回聚合相關的統計信息,甚至可以返回聚合的一個子集。
有人在倉儲查詢中使用匿名對象,使用匿名對象的原因很簡單,即為了進行投影操作,這樣可以限制Sql查詢返回的列,對於Sql性能調優來講,這可能非常重要,比如使用Sql Server的覆蓋索引。
但是使用匿名對象進行查詢有很多問題,一個問題是無法直接作為結果返回。有人為了省力,直接使用聚合或其部分子對象作為返回類型,其結果是對象中的一部分屬性被賦值,另一部分屬性為null。這可能導致許多神秘的Bug,另外導致你或其它人對倉儲查詢信心不足,因為這些查詢並不安全,你每當需要調用一個查詢,首先得仔細檢查代碼,看看哪些值是空的,以免踩到地雷。
如果要返回聚合的一個子集,需要單獨定義一些對象,以作為返回類型。工作量雖然比較大,但更加安全和清晰。
如果需要獲取的內容由多個聚合組成,這個查詢操作應該放入哪個倉儲中?這種情況放到哪個倉儲其實都不合適,更好的辦法是使用一個查詢服務,就好像跨越多個聚合的業務操作需要領域服務一樣。
總結
1. 倉儲的定義
倉儲表示聚合的集合。
2. 倉儲的優點
- 簡化數據操作
- 減少冗余查詢邏輯
- 降低耦合度
- 方便單元測試
3. 倉儲的要點
- 使用更具體的倉儲
- 倉儲查詢僅返回與其聚合高度相關的內容
由於倉儲是對數據操作的封裝,包括倉儲基礎,分頁,查詢擴展,查詢對象,查詢條件(規約模式),查詢條件應用(日期范圍與數值范圍查詢)等內容,所以需要多篇文章進行介紹。
.Net應用程序框架交流QQ群: 386092459,歡迎有興趣的朋友加入討論。
謝謝大家的持續關注,我的博客地址:http://www.cnblogs.com/xiadao521/