9

我有一個處理產品和產品類別的應用程序。對於這些我都有使用POCO定義的模型。存儲庫模式:模型關係的實現和延遲加載

​​

該應用程序使用的存儲庫訪問這些模型

// The interface implemented by the application's repository 
interface IProductRepository { 
    IEnumerable<Product> GetAllProducts(); 

    void Add(Product product); 
    void Remove(Product product); 
    void Save(Product product); 
} 

在產品類別,類型產品分類的類別屬性命名應該只裝載在需要時/訪問(延遲加載) 。我希望我的模型保持POCO並只包含模型的結構。

我採取了正確的方法嗎?
我應該只有產品類中的類別ID,並使用產品類別的單獨存儲庫來加載類別?

實現延遲加載和關係
現在我實現倉庫接口的返回擴展了產品類型,並具有通過資源庫實例延遲加載支持類型的對象。

誰應負責加載產品類別?

我對產品和類別存儲庫如何交互以實現延遲加載感興趣?他們應該互相引用還是應該有一個主存儲庫與兩個子存儲庫並將其傳遞給我的擴展模型類型?

你會採取什麼方法?(任何建議和批評表示歡迎)


我要指出,我希望應用程序可擴展,爲存儲庫和模型本身將成爲一個獨立的asembly所有接口。這意味着擴展器不能直接訪問模型類定義。

回答

4

一些意見和我的意見:

1)

我應該在產品類中的 類別的唯一ID和使用 一個獨立的存儲設備產品 類別加載類別?

號您正在使用的ORM(至少我認爲你這樣做)能夠通過類實例之間的,而不是由你使用那麼在關係的方式來查詢標識引用關係建模。將你的想法帶到最後的結果將意味着你從模型類中完全刪除所有的導航屬性,並且只有標量屬性,並且它們中的一些用作對象之間的鍵。這只是ORM中的「R」。

2)

現在我實現 倉庫界面的返回擴展了產品 類型和具有通過資源庫實例延遲加載 支持類型的對象 。

不確定這是什麼意思。 (我想看到的代碼片段,你如何做到這一點)。但我的猜測是,在派生Product類你注入總得參考庫,就像這樣:

public class ProductProxy : Product 
{ 
    private IProductRepository _productRepo; 

    public ProductProxy(IProductRepository productRepo) 
    { 
     _productRepo = productRepo; 
    } 

    // now you use _productRepo to lazily load something on request, do you? 
} 

嗯,這顯然是一個問題現在加載類別,因爲IProductRepository沒有方法來訪問它們。

3)

我感興趣的產品和 類別庫應如何互動 實現延遲加載?它們應該是 彼此引用還是我 有一個主存儲庫與兩個 子存儲庫,並將其傳遞到我的 擴展模型類型?

你ProductRepository和CategoryRepository看起來像一個普通的寶庫,它是隻爲一個單一的實體類型負責的情況下(在EF 4.1,這將是類似於DbSet<T>其中TProductCategory分別)。

我會避免在這些存儲庫之間引用,因爲無論何時添加新的實體或導航屬性,這可能最終都會導致複雜的回購引用。

我看其他兩個選項:

  • (你已經提到基本上什麼)有一個倉庫負責ProductCategory在一起。您仍然可以擁有您的通用存儲庫,但我會將它們視爲內部幫助器回購,並且只會將它們用作主存儲庫內的私有成員。這樣你可以有一組存儲庫,每個存儲庫都負責一些密切相關的實體。

  • 介紹一個Unit of Work,它能夠創建所有的通用存儲庫(又在EF 4.1,這將是類似的工廠方法DbContext.Set<T>()其中DbContext是工作單位),然後注入這一工作單位到您的派生實例:

    public class ProductProxy : Product 
    { 
        private IUnitOfWork _unitOfWork; 
    
        public ProductProxy(IUnitOfWork unitOfWork) 
        { 
         _unitOfWork = unitOfWork; 
        } 
    
        public Category Category 
        { 
         get 
         { 
          // ... 
          var productRepo = _unitOfWork.CreateGenericRepo<Product>(); 
          var categoryRepo = _unitOfWork.CreateGenericRepo<Category>(); 
          // you can pull out the repos you need and work with them 
         } 
         set { ... } 
        } 
    } 
    

我寧願第二選項,因爲在第一個選項,您可以在巨大的倉庫最終支持所有可能的關係的負荷。想想:Order有OrderItems,OrderItem有Product,Product has Category,Order有客戶,Customer有地址列表,Address有聯繫人列表等,等等...你也要求批評)

你正在編寫自己的ORM,還是你正在編寫一個應用程序?您的設計走向了一個可能變得非常複雜的方向,我認爲您正在重塑車輪。如果您打算使用EF或NHibernate(或其他ORM),那麼您正在創建現成可用的函數,您只能將抽象放在其上,而不會增加任何值。通過動態代理進行延遲加載是透明的,因爲您從不明確地使用代碼中的那些代理,您總是使用POCO實體。它們是不可見的,只在運行時才存在。爲什麼你想開發自己的懶惰加載基礎設施?

+0

#2。你的假設是絕對正確的,這正是我想要做的。 #4。我試圖通過提供一個帶有所需接口和模型的.dll來實現一個存儲庫(比這個例子中的複雜很多),然後使用MEF加載實現,從而使應用程序具有可擴展性。對於內部實現,我使用EF Code First,因此我的存儲庫將變得乾淨(將艱苦的工作留給EF)。非常感謝你爲這個偉大的答案! – 2011-05-10 20:05:38

0

我做的事情非常相似,在我的情況下,我有產品和產品類別的存儲庫。倉庫之外沒有人知道事情是如何加載的 - 懶惰或任何 - 所以你可以稍後改變它,如果你想。就我而言,由於訪問量非常大,我迫不及待地加載了這些類別,並且我按需加載了產品並緩存了一個小時左右。

2

NHibernate支持通過代理對象進行延遲加載(默認使用Castle DynamicProxy),該代理對象爲存儲庫操作的類的子類。 NHibernate要求您將成員標記爲virtual以支持此場景。我不確定哪些組件在需要時啓動加載調用,但懷疑它是代理實例。

我個人認爲標誌着成員virtual是一個小的價格爲懶加載功能

+0

@Russ我知道這些框架(也是實體代碼優先),這就是爲什麼我標記模型中的所有成員都是虛擬的,但我也對存儲庫應該如何交互感興趣? – 2011-05-10 17:02:44

+4

理想情況下,您應該僅爲您的聚合根擁有存儲庫。看看http://stackoverflow.com/questions/1958621/whats-an-aggregate-root。因此,假設您的存儲庫加載了一個帶有類別代理的'Product'實例,那麼在該屬性上調用'get'時,代理應該從DB加載實際實例並將其分配給該屬性。也許代理對象應該在內部保存與其相關的聚合根的ID。 – 2011-05-10 17:10:13

+0

我明白了,但我應該如何管理ProductsRepository和ProductCategoriesRepository實例並將它們傳遞給模型。他們應該住在哪裏才能夠彼此參考? – 2011-05-10 17:17:15

0

我希望我的模型,以保持POCO和 只包含 模型的結構。

我會問,爲什麼數據的形狀對這些類很重要?

通常,這意味着您只是直接將我們的數據庫模式暴露給整個應用程序,並在其頂部有一個非常薄的「模型」表單。

我建議一個實體包含的數據是私有的,應該被封裝,並且行爲是什麼應該是實體的重點。

此外,一次加載所有您需要的數據會更簡單明瞭。如果可能的數據量太多,那麼我建議您將太多的責任分配給一個實體,並且您需要進一步優化模型或添加一種機制來根據您所處的上下文檢索實體使用它。

我的意思是,如果您要查詢顯示數據,則需要暴露一些數據集。您可以在另一個上下文中查詢類似但不同的數據集。進一步說,如果您需要執行某個操作或修改,您的實體需要公開特定的行爲。但是,在另一種情況下 - 當您檢索實體以執行不同的操作或修改時,則需要暴露不同的特定行爲。

試圖將所有這些不同的用途合併到一個合同或接口中,或多或少地導致上帝對象實體。

您只需要某些顯示和某些操作的類別集合。它只是導致痛苦,試圖讓它一直暴露在外。