團(tuán)隊(duì)開發(fā)框架核心架構(gòu)—Repositories
Repository由來
- 數(shù)據(jù)庫只是對(duì)象的永久保存方式曙搬,就象我們打字時(shí)經(jīng)常需要存盤一樣笋婿,我們不能因?yàn)橐按姹P”而去關(guān)心“存盤文件格式(數(shù)據(jù)表結(jié)構(gòu))”贸伐。
- 我們應(yīng)該更聚焦在模型這個(gè)對(duì)象抡草,把所有對(duì)象的保存(冬眠)和調(diào)用(激活)交由Respository完成砚哗。
- 對(duì)象保存到數(shù)據(jù)庫交由專門的Repository倉儲(chǔ)來完成龙助,由Repository負(fù)責(zé)如何將對(duì)象分解成數(shù)據(jù)庫能夠保存的格式。
什么是倉儲(chǔ)
倉儲(chǔ)表示聚合的集合蛛芥。
倉儲(chǔ)所表現(xiàn)出來的集合外觀提鸟,僅僅是一種模擬,除了測試以外仅淑,沒有理由使用內(nèi)存中真正的集合來創(chuàng)建倉儲(chǔ)称勋。
不應(yīng)該為所有實(shí)體建立倉儲(chǔ),只有聚合才擁有倉儲(chǔ)涯竟。
倉儲(chǔ)用來重建已持久化的聚合赡鲜,而工廠用于新建聚合。
使用倉儲(chǔ)的優(yōu)點(diǎn)
直接使用Entity Framework的DbContext不是很好嗎庐船,為什么還要在DbContext的上方封裝一層倉儲(chǔ)呢银酬,這是否多此一舉?
很多使用EF的程序員確實(shí)是直接使用DbContext醉鳖,并且他們發(fā)現(xiàn)開發(fā)起來十分簡單捡硅,因?yàn)镈bContext的接口設(shè)計(jì)得非常優(yōu)雅,從接口上看盗棵,DbContext就好像所有實(shí)體集合的倉庫壮韭。
另一方面,很多使用了倉儲(chǔ)的朋友纹因,都是依葫蘆畫瓢喷屋,雖然創(chuàng)建了倉儲(chǔ),但并沒有體會(huì)到多大好處瞭恰。
下面簡要介紹使用倉儲(chǔ)將為你帶來的優(yōu)點(diǎn)屯曹。
從概念上簡化數(shù)據(jù)操作
倉儲(chǔ)模擬了某種聚合的集合,而DbContext包含了N種類型的集合惊畏。
與倉儲(chǔ)相近的一個(gè)數(shù)據(jù)訪問模式是數(shù)據(jù)訪問對(duì)象(DAO)模式恶耽,很多人認(rèn)為倉儲(chǔ)不過是數(shù)據(jù)訪問對(duì)象換了一個(gè)名詞而已,從技術(shù)上說的確如此颜启,倉儲(chǔ)的強(qiáng)大之處在于概念上更簡單偷俭。倉儲(chǔ)在概念上代表內(nèi)存中的集合操作,而數(shù)據(jù)訪問對(duì)象代表數(shù)據(jù)庫操作缰盏,很明顯涌萤,集合比數(shù)據(jù)庫在概念上更簡單淹遵。減少冗余查詢邏輯
如果直接使用DbContext,由于特定查詢邏輯沒有一個(gè)固定位置负溪,可能分散到任何地方透揣,這很容易造成冗余。
倉儲(chǔ)是封裝特定查詢邏輯的好地方川抡,對(duì)于特定的查詢邏輯辐真,放到與聚合相應(yīng)的倉儲(chǔ)中即可。降低耦合度
直接使用DbContext猖腕,所有調(diào)用代碼與EF實(shí)現(xiàn)高度耦合拆祈。
另一方面,由于DbContext能夠獲取任意實(shí)體倘感,這些實(shí)體可能位于聚合內(nèi)部放坏,這樣會(huì)破壞聚合的封裝性,同時(shí)在任意位置可以獲取任意對(duì)象老玛,由于缺乏約束力而導(dǎo)致更高的耦合淤年。方便單元測試
直接使用DbContext只能進(jìn)行集成測試,必須連接到真實(shí)數(shù)據(jù)庫蜡豹。而領(lǐng)域?qū)又怀钟袀}儲(chǔ)接口麸粮,所以測試時(shí)很容易替換成模擬實(shí)現(xiàn),從而避開數(shù)據(jù)庫镜廉。
IRepositoryBase.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
namespace Rdf.Domain.Repositories
{
/* Note: ChenQP
* 定義倉儲(chǔ)接口
* 通過倉儲(chǔ)模式弄诲,可以更好把業(yè)務(wù)代碼與數(shù)據(jù)庫操作代碼更好的分離,可以針對(duì)不同的數(shù)據(jù)庫有不同的實(shí)現(xiàn)類娇唯,而業(yè)務(wù)代碼不需要修改齐遵。
* 定義倉儲(chǔ)接口的代碼寫到Domain項(xiàng)目中,因?yàn)閭}儲(chǔ)接口是領(lǐng)域?qū)拥囊徊糠帧? */
public interface IRepositoryBase<T> where T : class
{
#region On C塔插、U梗摇、D、Before&After
void OnBeforeInsert(T entity);
void OnAfterInsert(T entity);
void OnBeforeUpdate(T entity);
void OnAfterUpdate(T entity);
void OnBeforeDelete(T entity);
void OnAfterDelete(T entity);
#endregion
#region 獲得實(shí)體的列表
List<T> GetAllList();
Task<List<T>> GetAllListAsync();
Task<List<T>> GetAllListAsync(Expression<Func<T, bool>> predicate);
IQueryable<T> GetAll();
IQueryable<T> GetAll(Expression<Func<T, bool>> predicate = null);
#endregion
#region 獲取實(shí)體的列表想许,支持分頁
IEnumerable<T> Get(string orderBy, int pageIndex, int pageSize);
IEnumerable<T> Get(Expression<Func<T, bool>> predicate, string orderBy, int pageIndex, int pageSize);
#endregion
#region 獲得單個(gè)實(shí)體
T Single(Expression<Func<T, bool>> predicate);
Task<T> SingleAsync(Expression<Func<T, bool>> predicate);
T FirstOrDefault(Expression<Func<T, bool>> predicate);
Task<T> FirstOrDefaultAsync(Expression<Func<T, bool>> predicate);
#endregion
#region 插入
T Insert(T entity);
Task<T> InsertAsync(T entity);
#endregion
#region 更新
T Update(T entity);
Task<T> UpdateAsync(T entity);
#endregion
#region 刪除
void Delete(T entity);
Task DeleteAsync(T entity);
void Delete(Expression<Func<T, bool>> predicate);
Task DeleteAsync(Expression<Func<T, bool>> predicate);
#endregion
#region 其他
int Count();
Task<int> CountAsync();
int Count(Expression<Func<T, bool>> predicate);
Task<int> CountAsync(Expression<Func<T, bool>> predicate);
long LongCount();
Task<long> LongCountAsync();
long LongCount(Expression<Func<T, bool>> predicate);
Task<long> LongCountAsync(Expression<Func<T, bool>> predicate);
#endregion
}
}
倉儲(chǔ)總結(jié)
倉儲(chǔ)被設(shè)計(jì)出來的目的伶授,提供一個(gè)類似于集合一樣的用于管理聚合的容器,封裝與數(shù)據(jù)庫打交道的邏輯
工廠用于創(chuàng)建對(duì)象流纹,而倉儲(chǔ)則用來重建對(duì)象
領(lǐng)域模型中只包含倉儲(chǔ)的接口定義
倉儲(chǔ)一般不負(fù)責(zé)事務(wù)處理
倉儲(chǔ)的查詢接口設(shè)計(jì)
- 簡單明確固定個(gè)數(shù)的參數(shù)糜烹,較常用
- Specification, Criteria漱凝,搜索條件動(dòng)態(tài)時(shí)