我想知道使用實體框架的項目中存儲庫模式的用處。對此有相反的說法 - 有人說EF是存儲庫和工作單元模式本身的實現,所以不需要將其包裝在下一個抽象層中,有人認爲它具有諸如DAL和BL分離以及更容易創建等優點的單元測試。根據我的經驗,我經常遇到以下的方法(一般,不僅在EF項目):實體框架vs存儲庫模式
Repository (DAL) <-> Service (BL) <-> Controller
Repository + Service + Type = Model
Repository有隻負責數據訪問,即方法:
public interface IUsersRepository
{
IEnumerable<User> GetAll();
User Get(int id);
User GetByLogin(string login);
void Update(User item);
void Delete(int id);
void Delete(User item);
// ...
}
通常情況下,有使用通用存儲庫來代替,該方法接收功能過濾,排序等
反過來服務使用信息庫,並且包含業務邏輯,即:
public interface IUsersService
{
// ...
bool VerifyPassword(string login, string password);
void ChangePassword(string login, string password);
// ...
}
據我瞭解,服務應該不會有什麼DAL操作 - 這意味着我們不應該從即返回倉庫IQueryable的集合,因爲這樣的查詢將外庫和單元測試執行不會是完全可靠的(之間的差異LINQ-to-Entities和LINQ-to-Objects)。
我在這裏看到了查詢效率的問題 - 我們通常會從數據庫中獲取比需要更多的數據,然後將其過濾到內存中。
例如,讓我們考慮bool VerifyPassword(字符串登錄,字符串密碼)方法。
在這種情況下,我們需要從數據庫中獲取整個用戶實體,例如,可以有50個屬性,僅用於密碼驗證目的。我們當然可以,創建庫許多方法,如:
string GetPasswordHash(string login)
或
bool VerifyPassword(string login, string passwordHash)
{
return db.Users.Any(x => x.Login = login && x.Password = passwordHash);
}
,而不需要從數據庫中獲取完整的實體,但在我看來,這可能是一個「小」的開銷。
我們也可以將VerifyPassword函數從服務移動到存儲庫 - 那麼我們應該問自己一個問題,是否需要兩個層 - 存儲庫和服務。但是如果我們合併它們,我們將失去分離DAL和BL層的益處 - 單元測試將是現實中的集成測試。所以也許它會更簡單(更好)將它全部保存在控制器中,並注入模擬的DbContext或使用像Effort這樣的單元測試?
我將非常感謝您的回答,您如何看待這一情況以及如何在您的項目中解決此問題。
UPDATE
我明白,存儲庫模式允許容易地改變像的nHibernate數據源/提供者以外,LINQ到SQL,平原ADO等。
但是你能告訴我你如何在你的倉庫API中實現排序和分頁嗎?我的意思是將排序和分頁規範傳遞給存儲庫的最佳方式是什麼?我看到有些函數爲LINQ函數使用了謂詞,但是這會將存儲庫與LINQ緊密結合 - 使用它與純ADO,存儲過程等將會產生問題。創建像GetUsersOrderedByNameDesc()等許多方法在我看來是瘋狂的。所以也許創建自己的規範類,僞造和傳遞排序/分頁標準,並在庫中處理它?如果是的話,你能給我提供一些實現的例子嗎?
我不確定實際問題是什麼,所以這是一個評論,而不是讓你更具體的答案。當然,這是一個不錯的主意,它擁有存儲庫層和單獨的服務層。提到的VerifyPassword是一個很好的服務方法的候選者,它只會在存儲庫語言而不是DAL語言中實現。 –
我不同意,但沒關係。它只是表明這個問題對於StackOverflow的Q&A格式來說太廣泛了。 –