2011-10-29 13 views
24

我正在使用ASP.NET Webforms + EF4開始一個新的Web項目。我試圖按照本教程將工作模式應用於工作模式單元:http://www.dotnetage.com/publishing/home/2011/07/05/6883/the-repository-pattern-with-ef-code-first-dependeny-injection-in-asp-net-mvc3.html工作單元和存儲庫模式對於大型項目非常有用嗎?

我想我有這個想法,但我的問題是,當我在模型中創建一個新對象時,我也有在工作單元的IDALContext中定義該對象?這不是快速發展的手段嗎?另外,如果您與多個開發人員一起工作,並且您不希望其他開發人員查看您的DAL,那麼您如何管理此問題?因爲在這個模式中,據我所知,當你在模型中創建一個新對象時,你還必須在本教程的IDALContext中定義它。對不起,我很困惑。

+1

提到的工作單元的實現是廢話。在我看來,工作單位不應該知道任何有關倉庫的信息!你必須在IDALContext中定義每個實體也是非常糟糕的:(對於NHibernate,你不必做這樣的事情。一般來說,工作單元和存儲庫模式對於大型項目很有用,你只是讀了一篇很糟糕的博客請嘗試找到其他解決方案 – Rookian

+0

@Rookian您如何使用NHibernate進行處理?您使用UoW/Repository模式還是僅使用普通的DAO? – Juri

+0

@Juri我使用Uow + Repositories。有關實現請參見http://codecampserver.codeplex.com/ – Rookian

回答

18

Martin Fowler將存儲庫的角色描述爲:「存儲庫在域和數據映射層之間進行中介,充當內存域對象集合。」在這個意義上,實體框架4.1公開的存儲庫。此外,英孚具有內置工作的統一性。因此,我的建議是忽略您在問題中提到的博客文章。

這樣的代碼不僅僅是無用的或毫無價值的,但危險的,因爲沒有任何好處添加到您的代碼,但依賴!

public interface IUnitOfWork:IDisposable 
{ 
    int SaveChanges(); 
} 

public interface IDALContext : IUnitOfWork 
{ 
    ICategoryRepository Categories { get; } 
    IProductRepository Products { get; } 
} 

要回答有一些抽象的域和數據映射層之間起中介作用,像個內存中的域對象集合是「大」項目一絕你的問題。如果在底層有一個UnitOfWork機制,可以幫助將業務邏輯從訪問中解耦出來,從而實現一些數據訪問抽象。

TL; TR; 存儲庫和UnitOfWork可以幫助您,但不會像在給定的博客文章中那樣應用它。

+5

+1指出,EF上下文既是一個存儲庫,也是一個工作單元,開箱即用。理解模式的關鍵之一是能夠在代碼中識別它們,即使沒有名爲模式的類 –

+0

有沒有在IDALContext中定義每個存儲庫的方法?當需要越來越多的存儲庫時,IDALContext會混亂起來,在我看來,IDALContext inte rface違反了「界面隔離原則」的SOLID原則之一。 – Rookian

+0

是的,有一種方法:就是不要使用它。例如,您可以在HTTP請求範圍中使用相同的DbContext,以確保您的工作單元完成您期望的任務。 – saintedlama

92

現在,第一個問題應該是,爲什麼我需要知識庫或工作單元模式?難道我不能僅僅使用控制器的EF上下文,具有直接編寫我需要的查詢並返回數據的全部功能嗎?
答案:你可以,但真正的意圖背後是可測試性,因此更高質量,更可維護的代碼。如果您將數據訪問分開並集中在一個地方,則可以在測試期間將其模擬出來。這使您可以單元測試您的控制器內定義的邏輯,而無需有效寫入數據存儲。

在開始使用工作單元之前,只需使用存儲庫模式即可。這基本上抽象了給定實體的數據訪問。因此,您可以定義諸如Filter(),All(),Update(..),Insert(..),Delete(...)以及Save()之類的方法。實際上,其中大部分可以很容易地抽象爲BaseRepository<TEntity>類,以至於最終只需在特殊行爲的罕見情況下創建新的Repository。否則它會像BaseRepository<Person> personRepo = new BaseRepository<Person>()BaseRepository<Address> addressRepo = new BaseRepository<Address>()

爲什麼需要工作單元?
工作單元通常表示每個Http請求在Web環境中在特定週期內完成的所有操作。這意味着當一個新的請求進入時,你實例化一個新的工作單元,添加新的東西,更新或刪除它,然後通過調用.save().commit() ..「無論如何」「改變」更改。實際上,如果仔細查看實體框架DbContext(或ObjectContext),它們已經代表了某種工作單元。
但是,如果你想進一步抽象它,因爲你不一定希望在你的控制器類中使用EF上下文(記住:可測試性),那麼你創建一個UoW來對你的Repositories進行分組,並且確保它們共享相同的EF上下文實例。您也可以通過DI容器(依賴注入容器)來實現後者。

回答您的問題:它在大型項目中有用嗎?
當然,特別是在項目。這是關於保持責任分開(數據訪問,業務邏輯,域邏輯),從而使事情可測試。

+1

這是一個該死的有用的帖子。感謝您的超級解釋! – bas

+0

您是否需要這種與CQRS的交易和最終的一致性? – inf3rno

+1

@ inf3rno不確定與「最終一致性」的關係。這完全是一種結構模式。因此,通過爲查詢和命令部分提供兩個不同的UoW,您也可以將它應用於CQRS。說實話,我從來沒有這樣用過。恕我直言,這裏的重要部分是知道這是爲了構建您的應用程序,以改善其設計 - >可測試性 - >可維護性。如果它在你的方式或使你的情況更復雜,只是不要使用它:) – Juri

1

軟件設計模式旨在解決具有正確上下文的特定問題,如果使用不當,會導致額外的不必要的複雜性而不提供任何值。

那麼,存儲庫模式旨在解決什麼問題?

1-最小化重複查詢邏輯:在大型應用程序中,您可能會發現很多複雜的LINQ查詢都在幾個地方複製。如果是這種情況,您可以使用存儲庫模式來封裝這些查詢並儘量減少重複。關注

2-更好的分離:想象一個複雜的查詢來獲取給定類別,涉及預先加載,連接,分組,過濾等最暢銷的課程

當你實現這樣的大型複雜在你的服務/控制器中查詢,你最終會得到大量的服務/控制器。這些類很難進行單元測試,因爲它們需要大量嘈雜的殘片。你的單元測試變得很長,很胖,不可維護。

如果您遇到此問題,或許您可能會考慮使用存儲庫模式。在這個例子中,我們可以封裝複雜的查詢來獲取最暢銷的課程資源庫:

courseRepository.GetTopSellingCourses(int categoryId, int count); 

這樣,你的服務/控制器將不再處理預先加載,連接,分組等相反,他們將委託給存儲庫。請記住,熱切加載,加入和分組等都是查詢邏輯,屬於您的數據訪問層,而不是您的服務或表示層。

3- 解耦從持久性框架應用程序體系結構:當您直接在應用中使用的實體框架的類(例如的DbContext,DbSet等),應用程序是緊耦合到實體框架。如果您計劃在將來某個時間切換到其他O/RM,或者更新版本的實體框架使用不同的模型,則可能需要修改應用程序的很多部分,這可能會導致應用程序出現新的錯誤。您可以使用存儲庫模式將您的應用程序體系結構與持久性框架(如Entity Framework)分離。這樣,您就可以自由地更改爲不同的O/RM,同時對應用程序的影響最小。

觀看此視頻,瞭解詳情:

https://youtu.be/rtXpYpZdOzM

0

你應該考慮「命令/查詢對象」作爲替代,你可以找到一堆在這附近有趣的文章,但這裏是一個好一個:

https://rob.conery.io/2014/03/03/repositories-and-unitofwork-are-not-a-good-idea/

你會堅持每命令啓用簡單的事務的單個命令對象,避免了單位工作模式的複雜性的需要。但是,如果您認爲每個查詢的查詢對象過度殺毒,那麼您完全可以100%正確。通常,您可能會選擇從「FooQueries」對象開始,該對象本質上是一個存儲庫,但僅適用於查詢。 'Foo'可能是DDD意義上的'域名聚合'。

稍後您可能會發現單個查詢對象值得。

與大多數情況下,您必須考慮系統的系統基礎。

相關問題