2015-10-07 196 views
3

我有一個服務,我希望能夠根據控制反轉原理創建,所以我創建了一個接口和一個服務類。IoC,依賴注入和構造函數參數

public interface IMyService 
{ 
    void DoSomeThing1(); 
    void DoSomeThing2(); 
    void DoSomeThing3(); 
    string GetSomething(); 

} 

public class MyService : IMyService 
{ 
    int _initialValue; 
    //... 

    public MyService(int initialValue) 
    { 
     _initialValue = initialValue; 
    } 

    public void DoSomeThing1() 
    { 
     //Do something with _initialValue 
     //... 
    } 

    public void DoSomeThing2() 
    { 
     //Do something with _initialValue 
     //... 
    } 

    public void DoSomeThing3() 
    { 
     //Do something with _initialValue 
     //... 
    } 

    public string GetSomething() 
    { 
     //Get something with _initialValue 
     //... 
    } 
} 

例如Unity我可以設置我的IoC。

public static class MyServiceIoc 
{ 
    public static readonly IUnityContainer Container; 

    static ServiceIoc() 
    { 
     IUnityContainer container = new UnityContainer(); 
     container.RegisterType<IMyService, MyService>(); 
     Container = container; 
    } 
} 

問題是構造函數參數。我可以使用參數覆蓋像

var service = MyServiceIoc.Container.Resolve<IMyService>(new ParameterOverrides 
{                     
    {"initialValue", 42} 
}); 

但我不想使用失敗類型的參數。如果某人更改構造函數參數名稱或添加一個參數會怎麼樣?他不會在補充時間發出警告,也許沒有人會檢測到它,但最終用戶。也許程序員爲了測試而改變了他的IoC設置,但忘記了它的「發佈」用法,那麼即使代碼覆蓋率爲100%的代碼庫也不會檢測到運行時錯誤。

可以將一個Init函數添加到接口和服務中,但是服務的用戶必須明白這一點,並且每次獲取服務實例時都要記得調用init函數。該服務變得不那麼自我解釋,並且對不正確的用法開放。如果方法不依賴於它們被調用的順序,我認爲最好。

讓它更安全的一種方法是在Ioc上創建一個Create函數。

public static class MyServiceIoc 
{ 
    //... 
    public IMyService CreateService(int initialValue) 
    { 
     var service = Container.Resolve<IMyService>(); 
     service.Init(initialValue); 

    } 
} 

但是,如果您只關注服務及其接口,上述問題仍然適用。

有沒有人有強大的解決方案來解決這個問題?如何以安全的方式將初始值傳遞給我的服務仍然使用IoC?

回答

3

DI容器是基於反射的,基本上是弱類型的。問題比Primitive Dependencies廣泛得多 - 它無處不在。

只要你做的東西像下面,你已經失去了編譯時的安全性:

IUnityContainer container = new UnityContainer(); 
container.RegisterType<IMyService, MyService>(); 
var service = container.Resolve<IMyService>(new ParameterOverrides 
{                     
    {"initialValue", 42} 
}); 

的問題是,您可以刪除第二個語句的代碼仍然編譯,但現在就不再起作用:

IUnityContainer container = new UnityContainer(); 
var service = container.Resolve<IMyService>(new ParameterOverrides 
{                     
    {"initialValue", 42} 
}); 

注意,缺乏編譯時的安全性無關,與具體的依賴,但一個事實,即d我參與了I Container。

這也不是Unity問題;它適用於所有DI容器。

There are cases where a DI Container may make sense,但在大多數情況下,Pure DI是一個簡單和更安全的替代:

IMyService service = new MyService(42); 

在這裏,你會得到一個編譯器錯誤,如果有人當你望着遠處的其他人更改API。這很好:compiler errors give you more immediate feedback than run-time errors


順便說一句,當你在一個原始依賴傳遞,無形中把它變成一個Concrete Dependency,你變得更加困難的客戶,瞭解發生了什麼事情。

我建議這樣的設計,而不是它:

public class MyService : IMyService 
{ 
    AnotherClass _anotherObject; 
    // ... 

    public MyService(AnotherClass anotherObject) 
    { 
     _anotherObject = anotherObject; 
    } 

    // ... 
} 

這仍然是很簡單的類型安全與純DI組成:

IMyService service = new MyService(new AnotherClass(42)); 
+0

謝謝!你關於AnotherClass的陳述可能是正確的,但這不是我關心的,這只是一個例子,因此我已經從我的例子中刪除了它。所以它不會被誤導。抱歉給你帶來不便! – AxdorphCoder

1

如何傳遞初始以仍然使用IoC的安全方式向我的服務提供價值?

您可以顯式調用類的構造函數,同時使用IUnityContainer.RegisterInstance方法統一註冊吧:

container.RegisterInstance<IMyService>(new MyService(42)); 

這會給你你提到的編譯時的安全性,但付出的代價是,它會僅實例化一次,並且將立即創建(而不是首次請求時)。

你也許可以通過使用方法重載之一來處理這個缺點,它接受一個LifetimeManager類。

+0

謝謝,但我必須能夠創建具有不同初始值的多個實例。 – AxdorphCoder