5

我有一個類在其構造函數中有一些基本類型參​​數,如字符串等。用原始參數構造函數註冊一個類型?

我應該如何註冊與統一容器的類型?

public LoginManager(
    IRegionManager regionManager, 
    IEventAggregator eventAggregator, 
    string mainRegionName, 
    Uri login, 
    Uri target) 
    { 
    this.regionManager = regionManager; 
    this.eventAggregator = eventAggregator; 
    this.mainRegionName = mainRegionName; 
    this.login = login; 
    this.target = target; 
    } 
} 

更新
Remeber的IRegionManagerIEventAggregator已知類型的棱鏡UnityBootstrapper這是在我的情況下,容器包裝。我必須重新註冊嗎?我想盡可能簡化類型註冊。

這會被認爲是一個壞習慣嗎?任何更好的選擇?

回答

12

說實話,我會盡量避免在構造函數中有一個原始類或難以解析類的類設計。正如你已經從Tavares的回答中看到的那樣,你的配置變得非常脆弱(更新:Tavares似乎已經刪除了他的回答,原因是我不清楚)。您會減少編譯時支持,並且每次更改該構造函數都會使您更改DI配置。

有多種方法可以更改您的設計以防止出現這種情況。哪一個是適用於你的是你的,但這裏有一些想法:

方法1:使用一個不變的配置DTO:

private sealed class LoginManagerConfiguration 
{ 
    public Uri Login { get; private set; } 
    public Uri Target { get; private set; } 
    public string MainRegionName { get; private set; } 

    public LoginManagerConfiguration(Uri login, Uri target, string mainRegionName) 
    { 
     this.Login = login; 
     this.Target = target; 
     this.MainRegionName = mainRegionName; 
    } 
} 

現在你可以讓你的LoginManager採取的依賴LoginManagerConfiguration

public LoginManager(IRegionManager regionManager, 
    IEventAggregator eventAggregator, 
    LoginManagerConfiguration configuration) 
{ 
    ... 
} 

LoginManagerConfiguration可以簡單地註冊這樣的:

container.RegisterInstance<LoginManagerConfiguration>(
    new LoginManagerConfiguration(
     login: new Uri("Login"), 
     target: new Uri("Target"), 
     mainRegionName: ConfigurationManager.AppSettings["MainRegion"])); 

指定應用程序範圍的配置對象而不是特定類型的DTO可能很誘人,但這是一個陷阱。這種應用程序範圍的配置對象是服務定位器反模式的配置等同物。變得不清楚類型需要什麼樣的配置值,並使得類難以測試。

選項2:從該類

另一種選擇是從類派生推導,只是爲了DI構造的目的。當你不能改變類簽名,這是特別有用的(即當它是一個第三方組件):

private sealed class DILoginManager : LoginManager 
{ 
    DILoginManager(IRegionManager regionManager, 
     IEventAggregator eventAggregator) 
     : base(regionManager, eventAggregator, 
      ConfigurationManager.AppSettings["MainRegion"], 
      new Uri("Login"), 
      new Uri("Target")) 
    { 
     ... 
    } 
} 

定義這個類接近你的應用程序的組成根源。該類成爲DI配置的實現細節。你的類型的註冊,現在將是非常簡單的:

container.RegisterType<ILoginManager, DILoginManager>(); 

要非常小心,雖然與調用,延遲加載配置值,如ConfigurationManager.AppSettings["MainRegion"]。這很容易導致在應用程序啓動過程中配置錯誤未被捕獲的情況,這確實是最好的。

方法3:使用一個工廠委託

最後的選擇我想現在是一個工廠。這看起來非常像Traveses的答案,但是更安全:

var mainRegion = ConfigurationManager.AppSettings["MainRegion"]; 

container.Register<ILoginManager>(new InjectionFactory(c => 
{ 
    return new LoginManager(
     c.Resolve<IRegionManager>(), 
     c.Resolve<IEventAggregator>(), 
     ConfigurationManager.AppSettings["MainRegion"], 
     new Uri("Login"), 
     new Uri("Target")); 
})); 

我希望這有助於。

+0

對於InjectionFactory方法,您還可以註冊一個類型爲Func 的關聯接受這些參數的InjectionFactory並返回一個ILoginManager,如下所示:new InjectionFactory(c =>(login,target ,mainRegionName)=> c.Resolve (新的ParameterOverride(「mainRegionName」,mainRegionName),新的ParameterOverride(「login」,登錄),新的ParameterOverride(「target」,target))))。通過這種方式進行解析,您可以在構造函數中保持依賴關係的靈活性,並且只需對基本參數更改進行更改即可。 – 2011-06-01 22:03:33

+1

非常好的答案,順便說一句。 – 2011-06-01 22:03:52

+0

忘記添加:使用方法是,您可以接受Func 作爲依賴項,並將這些參數傳遞給該函數,從而生成ILoginManager。如果這些是設置,我會堅持使用Steven的方法,但是如果它們的運行時值可能會在某些情況下發生變化,那麼您可能需要這樣做。 – 2011-06-01 22:06:01

0

不,這不是一個壞習慣。這是一個非常有效的場景。從Unity開始已經過去幾年了,但從頭頂開始,你必須明確地指出你想要的構造函數,並枚舉所有的參數,而對於原始的構造函數,請做new ResolvedParameter("your value")

另外我注意到你有一個Type參數。對Unity的態度要謹慎,因爲它具有......非常令人驚訝的處理方式。我有一個博客詳細說明here

+0

dziękuję。請看看我更新的問題。如果您能提供我的特例,我將不勝感激。順便說一句,哪裏是*我的頭頂*?我認爲它被稱爲堆? – Shimmy 2011-05-15 21:37:39

+0

絕大多數情況下,您不需要明確使用ResolvedParameter來處理基元。 – 2011-05-15 21:43:38

相關問題