![](/img/trans.png)
[英]Updating navigation properties that are collections in Entity Framework with Repository pattern
[英]Entity Framework, Navigation Properties, and the Repository Pattern
我正在努力找出實體框架和存儲庫模式的理想實現。 我正在使用Entity Framework 4.3代碼優先,而我似乎無法完全理解實體框架的正確用法。
我喜歡EF給桌面帶來的東西,比如跟蹤實體,延遲加載,導航屬性等等。但是根據我的理解,其中一些對於存儲庫模式並不好。 讓我們看一些例子,也許你們可以讓我直截了當。
我對通用存儲庫的初步印象是我不喜歡它,因為我不需要為每個實體提供完全相同的功能。 例如,我有一個存儲庫,用於在數據庫中存儲簡單變量(鍵/值對)。 我不需要Add或Delete方法,因為這些是靜態變量。 我只需要一個Update方法和一個Get方法。 通用存儲庫似乎不是很健壯,並且不允許數據層中有太多自定義代碼。 我也討厭通用存儲庫返回IQueryable<T>
因為它使上層能夠直接針對數據存儲編寫表達式,而上層必須假設正確使用的數據訪問技術實現IQueryable,以便它查詢數據庫,而不是將所有內容都拉入內存並從那里查詢。
它看起來像通用存儲庫,特別是返回IQueryable的存儲庫,並不真正堅持良好的關注點分離。 也許你們可以為我清除那個,但是現在我正在使用顯式命名的存儲庫,只返回IEnumerable或IList。
我喜歡導航屬性的概念,但在實現存儲庫模式時,我似乎很少使用它們。 例如,我有一個名為“Aliases”的導航屬性的用戶。 如果我想為用戶添加別名,通過導航屬性添加它會非常容易。
myUser.Aliases.Add(new Alias { Name="cls", Value="ClearScreen" });
但那我在哪里調用dbContext.SaveChanges()
? 我將myUser
傳遞給了我,我使用了導航屬性,以避免將我的IAliasRepository
注入到我IAliasRepository
的類中。但是我現在無法將我的新別名保留到數據庫中,因為我的上層不知道實體框架。 我現在必須注入我的IAliasRepository
,所以我可以_aliasRepository.SaveChanges()
。 那么現在感覺就像完全浪費。 我覺得我應該使用_aliasRepository.AddAlias(newAlias)
因為我必須要注入存儲庫。
自我跟蹤實體非常棒,但它們並不適合您試圖隱藏應用程序其余部分的數據訪問層詳細信息的應用程序。 例如,如果我正在編寫存儲庫並且完全不知道他們將使用EF,那么我肯定會添加一個Update(Entity entity)
方法。 但是,在EF中,您不需要這樣做,因為您只需對實體進行更改,然后調用SaveChanges()
。 該實體跟蹤已修改的所有內容,並將這些更改持久保存到數據庫中。
var myEntity = _entityRepository.GetEntity("some unique ID");
myEntity.SomeProperty = "new value";
_entityRepository.SaveChanges();
這導致我消除了我將包含的更新方法,如果我不知道EF不需要它們。 這使得重新分解更加困難,因為我可能必須返回並添加適當的更新方法。 我唯一的另一種選擇是無論如何都要包含這些方法,然后在實現我的存儲庫時對它們不做任何處理。
public void UpdateEntity(Entity entity)
{
// Do nothing. EF is tracking changes and they will be persisted when
// SaveChanges() is called.
}
所以我的代碼看起來像這樣,即使它完全沒必要。
var myEntity = _entityRepository.GetEntity("some unique ID");
myEntity.SomeProperty = "new value";
_entityRepository.UpdateEntity(myEntity);
_entityRepository.SaveChanges();
我想假設一個空方法並不可怕,如果我只是為了保持適當的關注點分離以便以后進行簡單的重構,但這樣做仍然感覺很有趣。
這種模式的另一個奇怪的怪癖是你必須要特別小心你的DbContext。 需要將相同的實例注入所有存儲庫。 否則,如果您將實體從一個存儲庫中拉出來並嘗試將它們與另一個存儲庫中的實體相關聯,那么它們將不能很好地協同工作,因為它們來自不同的DbContext實例。 IoC容器使這更容易控制,但對於剛開始使用EF的開發人員來說,這是一個奇怪的問題。 這里不是一個真正的問題,只是與Entity Framework和存儲庫模式的另一個奇怪之處。
使用EF正確實現存儲庫模式是什么? 你如何克服這些障礙?
通用存儲庫與非通用存儲庫
通用存儲庫不是模式。 通用存儲庫只是一個包裝器 。 它對於某些特殊情況作為特定存儲庫的基類非常有用,但在大多數情況下它只是過度使用而且過於誇張的廢話。
導航屬性
存儲庫本身應與聚合根一起使用。 聚合根是多個相關實體的聚合,其中您只有主體的存儲庫,因為沒有父項,依賴關系不能存在。 存儲庫本身處理聚合中的所有實體類型的加載和持久化。
即使有了根源,你也會遇到一些挑戰。 例如,如何處理多對多關系? 多對多關系總是表示沒有真正的主體或依賴實體的情況=它們不在聚合中。 如果你只通過導航屬性設置這兩個實體之間的關系它仍然可以,但EF也允許你通過導航屬性創建相關的實體,它不知何故違反了存儲庫的目的。 您可以強制執行存儲庫以測試關系的存在,但它可能會導致許多其他查詢,因此您很可能將其作為實現的漏洞抽象。
自我跟蹤實體
從您的代碼中我認為您將自我跟蹤實體與附加實體混淆。 您描述的是附加實體和分離實體之間的區別。 如果要在單個代碼中支持這兩種方案,則UpdateEntity
方法具有意義,因為您必須測試是否附加了實體並附加了它+如果沒有則附加設置狀態。
保持DbContext同步
這是你缺少工作單位的地方。 存儲庫本身僅包裝查詢並將實體存儲到上下文中,但工作單元處理上下文創建/退役和持久更改數據庫。 例如,您可以使用DbSet
(EF的存儲庫實現)和DbContext
(EF的工作單元實現)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.