3

我使用Unity作爲依賴注入引擎。它包含'repository'和'manager'類的接口/類。這些存儲庫負責從/到DB中獲取/保存/更新數據,管理員知道與其他組件/類的對象關係。依賴容器:如何實例化對象實例

有工廠實現來獲取依賴容器的實例(每個請求有1個實例)。當任何存儲庫/管理器類被實例化時,它接收依賴性容器作爲構造器參數,並且可以在需要時創建所有其他管理器/存儲庫。

有2種方式來創建業務實體:

  1. 當數據從DB倉庫中取出,創建對象的實例,並適當DB數據初始化它們,等之間的「容器」字段被初始化。
  2. 當在代碼中創建對象以將其放入DB中時,它接受「IUnityContainer」參數。

因此,在這兩種情況下,任何業務對象都有依賴容器,如果他需要獲取任何數據,他可以獲得適當的管理器實例並請求所需的數據。

問題描述:上週我看了一些問題/上,這樣的狀態是這樣的答案:

  1. 對象實例不應該訪問的依賴容器;
  2. 相反,他們應該在構造函數中接收所有必需的接口。

猜測,這同樣適用於管理器/存儲庫類:我不應該將容器實例傳遞給它們的構造函數,而應該將接口放到其他所需的組件中。

對我來說,這似乎是合理的,但:

  • 我需要爲任何目的是提供他所需要的所有接口(通常,每個經理/庫/實體需要他們的3-5);

  • 有很多情況下,只需要1-2接口(所以我不想創造很多其他);

問題1:真的是很好的辦法來 「隱藏」 的容器?爲什麼? (其實,我覺得我知道爲什麼,但如果你有好的答案,請告知)。

問題2:解決此類問題的最佳做法是什麼? (我使用Linq2Sql,並考慮切換到EF)我不能通過DependencyContainer創建對象,我自己應該這樣做(因爲對象是通過使用在SQL站點上執行的表達式創建,將使用分配給公共屬性的以下數據調用無參數構造函數;並且SQL方面不存在依賴項容器)。有沒有解決方法?

我實現的技術細節:

經理基類構造函數的例子:庫基類的

public abstract class ManagerBase : IManager 
{ 
    protected ManagerBase(IUnityContainer container) 
    { 
     DependancyContainer = container; 
    } 

    protected readonly IUnityContainer DependancyContainer; 

    ... 
} 

例子:

public abstract class RepositoryBase<T, TDb> : IRepository<T> 
    where T : IEntity 
    where TDb : class, IDbEntity, new() 
{ 
    protected abstract ITable<TDb> GetTable(); 

    public IQueryable<T> GetAll() 
    { 
     return GetTable().Select(GetConverter()); 
    } 

的數據是如何從數據庫中提取實例:存儲庫創建對象的實例並使用適當的DB數據初始化它們,其中「容器」字段已初始化:

public class CountryRepository 
    : RepositoryBase<ICountry, DbData.Country>, ICountryRepository 
{ 
    protected override Expression<Func<DbData.Country, ICountry>> GetConverter() 
    { 
     return dbEntity => new Country 
     { 
      DependancyContainer = DependancyContainer, 

      Code = dbEntity.CountryCode, 
      Name = dbEntity.CountryName, 
     }; 
    } 

以下是調用時需要的數據是從數據庫得到:

public class CountryManager : ManagerBase 
{ 
    ICountry GetCountryById(int countryId) 
    { 
     ICountryRepository repository = DependancyContainer.Resolve<ICountryRepository>(); 
     return repository.GetAll() 
      .Where(country=>country.Id==countryId) 
      .SingleOrDefault() 
      ; 
    } 
} 
+0

我想回答你的問題,但不清楚'*有很多情況下只需要1-2個接口(所以我不想創建很多其他);「 ...創建很多其他特別的東西? – 2011-03-17 21:33:56

+0

另外我認爲你的示例'CountryManager'應該擴展'ManagerBase'? – 2011-03-17 21:52:56

+0

CountryManager是從managerBase繼承的,這是正確的 – Budda 2011-03-19 14:47:25

回答

2

Q1:其他人可能不同意,但一般情況下不應到你的域模型/實體提供您的DI容器類。我看到有兩個主要原因:

  1. 將容器賦給實體類會混淆實體類的依賴關係。如果您依賴構造函數注入,您可以通過查看構造函數來查看它的依賴關係。如果你依賴屬性注入,你可以通過查看屬性來了解它的依賴關係,雖然這不如構造器注入清楚(即一些屬性是實體類的數據,但其中一些屬性是其他的接口爲實體類提供服務;在分離兩類時存在一些認知負擔)。如果您將容器提供給實體,那麼依賴關係將散佈在整個課程中。
  2. 更簡單的測試。如果您將容器傳遞給實體類,那麼爲了測試它,您將不得不模擬/存根容器以及它從容器中檢索的接口。您始終可以設置一個輕量級容器,但這不僅僅是直接提供模擬/存根接口,還有更多的工作。

Q2:有人會說,好的做法(DDD在哪裏存儲庫模式/強域模型模式來源於)不暴露你的實體類接口擺在首位。相反,他們應該包含可以實現的任何業務邏輯而不依賴於其他接口,並且更復雜的邏輯應該在Service類中(按照DDD的說法)。

Q3:你沒有說你正在使用哪種對象關係映射工具,但可能通過某種攔截器/面向方面的編程模式來控制實體對象實例化過程。然後,這會使您的代碼庫更接近依靠這個特定的O/R映射器及其功能(即,儘管我在構建架構時通常傾向於避免擔心這種情況,但難以從其軌道上改變它)。關於你的示例代碼

一些更多的意見,可能會或可能不表示你的代碼的其餘部分:

  • 你看AutoMapper?您可能會發現,您在CountryRepository.GetConverter()課程中正在執行的手動映射更好。
  • 看起來您正在爲DbData名稱空間中的每個類建立並行實體類,大概是因爲您無法將要在DbData命名空間類中插入的服務。也許我對Q2的回答會引導您朝着更好的方向發展(如果可能,請依靠服務並擴展DbData課程)。
  • 我不確定Manager正在播放什麼角色......理想情況下,您的所有查詢方法應該在存儲庫上,以便他們實際上可以對數據庫執行SELECT ... FROM Country where Id = ?查詢,而不是返回整個表並執行查詢在內存中通過LINQ,畢竟這是數據庫擅長的! (儘管針對您的應用程序的性能特徵進行了有意的緩存優化)。 GetCountryById方法應該在CountryRepository本身我相信。

希望有幫助!

+0

存儲庫任務是提供映射。也許你是對的,我最好將諸如「GetCountryById」之類的函數從管理者移動到存儲庫。但是,我的經理人的目標是返回一個完整的對象。如果我將返回「訂單」實體,它將不會一致,沒有所有的訂單記錄...所以「訂單管理器」將做2件事:從訂單本身(從訂單倉庫)獲取DB數據並從數據庫訂單獲取 - 記錄(來自OrderRecordRespotitory)。 OrderRepository只執行順序對象映射。 – Budda 2011-03-19 14:43:43

+0

作爲數據映射器我使用Linq2Sql。順便說一句,謝謝,如果我的意見有價值,等待其他想法 – Budda 2011-03-19 14:50:15

+0

也許你應該更多地瞭解域驅動設計,有一個概念calles有界的上下文和聚合根。並非您域中的每個類都應該有自己的存儲庫(恕我直言),而是確定聚合根(您稱爲FULL對象)在您的域中並僅爲它們提供存儲庫。這樣的存儲庫將負責持久和檢索構成根本根的所有模型。 – 2015-11-13 21:43:35