2009-02-17 50 views
7

我有一個問題,我打算標記這個主觀因爲這是我認爲它演變成更多的討論。我希望有一些好的想法或者一些思想敏捷的人。我對這個冗長的問題表示歉意,但你需要知道上下文。ServiceContainer,IoC和一次性物品

的問題基本上是:

  • 如何在關係處理的具體類型IoC容器?具體而言,誰負責處理它們,如果需要處置,以及這些知識如何傳播到調用代碼?

您是否要求他們是IDisposable?如果不是,那麼這個代碼是未來的證明,還是你不能使用一次性對象的規則?如果您對接口和具體類型強制實施IDisposable-requirements以確保未來不會受到影響,其責任是作爲構造函數調用的一部分注入的對象?


編輯:我接受了答案通過@Chris Ballard因爲它是最接近的一個,我們結束了與方法。

基本上,我們總是返回一個類型,看起來像這樣:

public interface IService<T> : IDisposable 
    where T: class 
{ 
    T Instance { get; } 
    Boolean Success { get; } 
    String FailureMessage { get; } // in case Success=false 
} 

然後,我們從兩個.Resolve和.TryResolve返回一個對象實現了這個接口回來,讓我們在調用代碼得到的是總是相同的類型。

現在,實現此接口的對象IService<T>是IDisposable,並且應該總是被丟棄。這不取決於程序員決定是否應該處置對象IService<T>

但是,這是至關重要的部分,服務實例是否應該被處置,知識是否被烘焙到實現IService<T>的對象中,所以如果它是一個工廠範圍的服務(即每次調用Resolve都會結束使用新的服務實例),那麼服務實例將在處理對象時丟棄。

這也使得它可以支持其他特殊的範圍,如池。現在我們可以說我們需要最少2個服務實例,最多15個,通常是5個,這意味着每次調用.Resolve都將從可用對象池中檢索服務實例,或者構建一個新實例。然後,當處理存儲池服務的對象IService<T>被丟棄時,服務實例被釋放回其池中。

當然,這讓所有的代碼是這樣的:

using (var service = ServiceContainer.Global.Resolve<ISomeService>()) 
{ 
    service.Instance.DoSomething(); 
} 

但它是一個乾淨的方式,它具有相同的語法,無論服務還是在使用具體對象的類型,所以我們選擇了爲可接受的解決方案


原題如下,供後人


囉嗦問題來了這裏:

我們有我們使用IoC容器,最近我們發現數額是多少到一個問題。

在非IoC的代碼,當我們想用,比如說,一個文件,我們使用這樣的類:

using (Stream stream = new FileStream(...)) 
{ 
    ... 
} 

有作爲這一類是否是一些持有有限毫無疑問資源與否,因爲我們知道文件必須關閉,並且類本身實現了IDisposable。規則就是我們構造一個實現IDisposable的對象的每個類都必須被丟棄。無話可問。這不是由該類的用戶決定是否調用Dispose是可選的。

好吧,繼續走向IoC容器的第一步。假設我們不希望代碼直接與文件交談,而是要經過一層間接尋址。在這個例子中,我們稱這個類爲BinaryDataProvider。在內部,類使用的流,這仍然是一個一次性對象,所以上面的代碼將被更改爲:

using (BinaryDataProvider provider = new BinaryDataProvider(...)) 
{ 
    ... 
} 

這並沒有太大變化。類實現IDisposable的知識仍然存在,沒有問題,我們需要調用Dispose。

但是,讓我們假設我們有類提供目前不使用任何有限資源的數據。

上面的代碼然後可以寫爲:

BinaryDataProvider provider = new BinaryDataProvider(); 
... 

OK,到目前爲止好,但問題來了的肉。假設我們想使用IoC容器來注入這個提供者,而不是依賴於特定的具體類型。

的代碼將被:

IBinaryDataProvider provider = 
    ServiceContainer.Global.Resolve<IBinaryDataProvider>(); 
... 

請注意,我假設有可用的,我們可以通過訪問該對象的獨立接口。

隨着上述變化,如果我們稍後想要使用真正應該處置的對象呢?沒有解決該接口的現有代碼被寫入來處理該對象,那麼現在呢?

我們看到它的方式,我們必須選擇一個解決方案:

  • 實現運行時檢查來檢查,如果正在註冊一個具體類型實現IDisposable,要求該接口是通過還器具暴露IDisposable接口。這不是一個很好的解決方案被用於
  • enfore表示對接口的限制,他們必須自IDisposable繼承,爲了成爲面向未來的
  • 強制運行時,它沒有具體類型可以是IDisposable的,因爲這是專門不由代碼使用IoC容器處理
  • 只需讓程序員檢查對象是否實現了IDisposable和「做正確的事情」?
  • 有其他嗎?

另外,如何在構造函數中注入對象?我們的容器和我們研究過的其他一些容器能夠將新的對象注入到具體類型的構造函數的參數中。例如,如果我們的BinaryDataProvider需要一個實現接口的對象,那麼如果我們對這些對象執行IDispose-「ability」,這些對象的職責是處理日誌記錄對象?

您認爲如何?我想要好的和壞的意見。

回答

2

(聲明:我是基於java的東西回答這個雖然我編寫C#我還沒有代理在C#什麼,但我知道這是可能有關Java術語對不起。)

你可以讓國際奧委會框架檢查正在構建的對象,以查看它是否支持ID爲一次性的 。否則,您可以使用動態代理來將IoC框架提供的實際對象包裝到客戶端代碼中。這個動態代理可以實現IDisposable,這樣你就可以隨時向客戶端提供一個IDisposable。只要你使用的接口應該相當簡單?

然後,您只需要向開發人員傳達問題時對象是IDisposable。我不確定如何以良好的方式完成這件事。

3

一個選擇是去同一個工廠模式,所以直接由IoC容器永遠不需要創建的對象被佈置自身,例如

IBinaryDataProviderFactory factory = 
    ServiceContainer.Global.Resolve<IBinaryDataProviderFactory>(); 
using(IBinaryDataProvider provider = factory.CreateProvider()) 
{ 
    ... 
} 

缺點是增加了複雜性,但它確實意味着容器永遠不會創建開發人員應該處理的任何東西 - 它始終是這樣做的明確代碼。

如果你真的想使它明顯,工廠方法可以被命名爲CreateDisposableProvider()。

+0

IoC容器的要點是我稍後通過配置可以返回不同的對象。在你的例子中,每個這樣的對象都必須實現IDisposable。我的問題是:這是否是這些對象的標準?我可以在某種程度上指定這個標準,還是每個界面?面向未來的? – 2009-02-17 13:27:29

+0

在這個例子中,我可以返回工廠的不同實現,但每個實現都必須創建一個實現IDisposable的提供者。這並不理想,但至少可以爲您提供關於語義的線索。也許這不是你問的問題? – 2009-02-17 13:53:48

1

你實際上想出了一個非常骯髒的解決方案:你的IService合同違反了SRP,這是一個很大的禁忌。

我推薦的是將所謂的「單件」服務與所謂的「原型」服務區分開來。 「singleton」的生命週期由容器管理,容器可以在運行時查詢特定實例是否實現IDisposable,如果是,則在關閉時調用Dispose()

另一方面,管理原型完全是調用代碼的責任。