2013-09-30 40 views
2

傳遞同一接口的多個實現我們公司的產品之一是由許多小型Web應用程序和一個windows服務組件組成,每個組件可能都駐留在不同的計算機中。其中之一是一個WebForms項目,作爲所有其他項目的配置中心。使用DI

我們正在設計一個功能來公開組件的一般信息。想象一下,一個簡單的界面,這樣,例如:

public interface IStatistics 
{ 
    Statistics GetStatistics(); 
} 

我們想用這個相同的接口上的所有組件,所以這是集中在一個共同的,共享的組件。這個實現最初也是一樣的,所以它在同一個程序集中,與界面一起。

接下來的想法是在每個組件上公開一個Wcf服務,同時使用通用程序集上的實現和接口。這個實現使用環境類,根據它們運行的​​地方返回不同的東西,比如本地機器時間。

我想優雅地解決的問題是如何使用相同的接口將每個組件的所有實現傳遞給webform。

我們目前使用Unity,但我想我會遇到與其他DI解決方案相同的問題。我想注入5個相同接口的實現(每個組件一個接口),並且能夠通過組件區分它們(認爲是Dictionary<Component, IStatistics>,其中ComponentEnum)。這是必要的,因爲頁面上會有一個下拉菜單來選擇哪個組件的信息是可見的,然後頁面會調用正確的實現來檢索結果。

我知道我可以使用所有實現的命名註冊,然後注入它們。可惜的是,這將導致我:

  1. 網頁上有5個不同的參數,每個都指向一個組件
  2. 註冊在容器上每個實現用不同的名稱
  3. 明確登記在容器上我的Web窗體,與自定義InjectionConstructor指定每個註冊接口,以正確的順序,對於頁注射方法

我知道統一具有ResolveAll方法,從而允許一個接收IStatistics[]IEnumerable<IStatistics>,但後來我無法區分它們。

我想MEF用元數據接口的概念解決了這個出色的問題。也許讓MEF在這上面解決這個問題?我認爲這樣做會不合適,因爲這些是我們正在討論的wcf代理,我看不出這將如何與MEF整合。

也許使用工廠將是一個更好的策略,但我不明白我將如何注入工廠的服務,所以問題會持續存在。

+1

如果命名是問題,爲什麼不用'DisplayNameAttribute'來修飾'IStatistics'實現?你可以讓你的工廠讀取這些屬性並返回一個IEnumerable >或類似的東西。 – Steven

+0

@Steven請注意,在我的問題中,我表示實現也集中在通用程序集中,因爲它實際上並不需要專門化。如果我在每個組件上實際實施了多次,您的方法纔有效。目前,這是沒有意義的。不過謝謝你的洞察力,否則它可能是一個不錯的方法。 – julealgon

+1

@julealgon,你怎麼知道有多少IStatistics代理以及他們的顯示名稱在下拉列表中? –

回答

2

我想知道,你是否可以設計整個事情略有不同。我認爲你想要做的是對IoC的看法。如果我正確理解你,你需要五個相同類型的實例,你可以以某種方式與另外五種不同類型的對象相關聯。 'IStatistics'是接口,定義在一個通用程序集中。執行,如Statistics是在同一個程序集。不同類型的五個實例在它們自己的「本地」組件中定義,這些組件由DI解決並且不直接引用。

這裏有一個想法,這可能是更簡單,實現你所需要的,而其餘的可維護,可擴展的,特別是如果你在某些時候需要爲每個組件類型不同的實現方式:

UML

DefaultStatisticsProvider能被組件用來實現IStatisticsProvider接口。這裏沒有涉及DI,因爲實現在公共項目中可用。

public class DefaultStatisticsProvider : IStatisticsProvider 
{ 
    public Statistics GetStatistics() 
    { 
     var statistics = new Statistics(); 
     // Generate statistics data 
     return statistics; 
    } 
} 

組件直接實現IStatisticProvider和繼電器的方法,他們在其構造函數初始化DefaultStatisticsProvider類型的私有字段:

public class ComponentA : IStatisticsProvider 
{ 
    private readonly DefaultStatisticsProvider _statisticsProvider; 

    public ComponentA() 
    { 
     _statisticsProvider = new DefaultStatisticsProvider(); 
    } 

    Statistics IStatisticsProvider.GetStatistics() 
    { 
     // You could change this implementation later to 
     // use a custom statistics provider 
     return _statisticsProvider.GetStatistics(); 
    } 
} 

現在,您可以將您的組件IStatisticsProvider直接註冊和不必須維護某種類型的統計提供程序實例的人工查找表,因爲您的類型統計提供程序,這對我而言也是合乎邏輯的。像(僞代碼)的東西:

Container.Register<ComponentA>().As<IStatisticsProvider>(); 
Container.Register<ComponentB>().As<IStatisticsProvider>(); 

使

Container.ResolveAll<IStatisticsProvider>(); 

會給你

{ Instance of ComponentA, Instance of ComponentB } 

此外,如前所述,如果你在某些時候需要有自定義實現提供統計數字,根本不需要重新設計。您只需更改組件中的GetStatistics()的實現。

+0

寫得很好的答案;)。稍後我會深入研究它,但是OP的重點在於避免不必要的代碼重複。由於實現是相同的,所以通過在每個組件上實現接口,我將獲得很少的收益。另外,我想知道如何用組件提供「下拉列表」,並使用這種方法調用正確的實現,您是否可以詳細闡述一下?也許你的想法和Steven的想法相結合,關於在實現上放置一個屬性(或者甚至是我試圖避免的界面屬性)。 – julealgon

+0

此外,我想補充一點,目前,我們實際上在每個組件上都有不同的接口,每個接口都從共享程序集中的原始接口繼承。這隻能夠區分容器上的註冊並將所有類型化的實現傳遞給網頁。這無疑是一個非常糟糕的解決方案,但它正在爲我所需要的工作:( – julealgon