2011-04-26 24 views
6

在Ninject中,聲明單例作用域中的綁定意味着每次都會返回同一個對象。只能有一個對象,永遠。如何創建一個Ninject自定義作用域,該對象作用域返回相同的對象直到該對象被丟棄?

我想要的是一次返回一個對象。換句話說:

  1. 第一次調用Get()實例化一個新對象並返回它。
  2. 對Get()的後續調用返回相同的實例。
  3. 物體被丟棄。
  4. 在處理對象後第一次調用Get()會實例化一個新的/第二個對象並返回該對象。
  5. 的後續調用獲取()返回在步驟創建的對象4.

編輯:這個問題實際上是相當簡單使用providers以及具有設置時所討論的對象引發事件使用來解決。我很好奇,如果在Ninject中有一種使用範圍的方法,並且會在這裏留下這個問題,因爲Steven的回答非常好。

+0

你正在運行什麼類型的應用程序?它是一個多線程應用程序嗎? – Steven 2011-04-26 06:34:11

+0

是的。像每個線程實例這樣的東西在這裏沒有用處。 – anthony 2011-04-26 06:43:05

回答

6

由於您希望在多線程應用程序中使用此構造,並希望跨線程重複使用同一實例(如您在註釋中所暗示的那樣),因此您將無法通過配置DI容器來解決此問題。

由於競爭條件,您無法在丟棄後配置要更新的對象。想象一下下面的場景:

  1. 線程1從容器中請求一個實例。
  2. 這是第一個請求,容器將創建一個新實例。
  3. 線程2從容器
  4. 請求的實例的容器返回步驟創建的實例2.
  5. 線程1與實例來完成,並調用Dispose
  6. 線程2開始使用該實例,但實例已處理,並引發異常。

問題是應用程序將獲得對可以處置的實例的引用。

儘量避免通過重新設計您的應用程序來做到這一點,如果可以的話。公開實現IDisposable的服務類型是一種不好的做法,因爲IDisposable是泄漏抽象。我個人的偏好甚至是阻止這些服務的任何實現來實現IDisposable。在大多數情況下,重新設計可以防止您必須這樣做。

如果您需要使用IDisposable對象,通常的方法是創建並注入創建這些對象的工廠。這樣消費者就可以安全地處理這樣一個對象,沒有任何問題。

這裏的一般問題是很難創建實際上是線程安全的實現IDisposable的對象。

如果你真的想要這個,你可以嘗試創建一個引用計數的裝飾器。在下面的修飾器中查找實例。它包裝IService並實施IServiceIService執行IDisposable。裝飾器需要一個允許創建實例的委託。對象的創建和處理受lock聲明的保護,並且裝飾器計算調用者對它的引用。在最後一位消費者處理裝飾器後,它將處理該對象並創建一個新對象。

public class ScopedServiceDecorator : IService 
{ 
    private readonly object locker = new object(); 
    private Func<IService> factory; 
    private IService currentInstance; 
    private int referenceCount; 

    public ScopedServiceDecorator(Func<IService> factory) 
    { 
     this.factory = factory; 
    } 
    public void SomeOperation() 
    { 
     IService instance; 
     lock (this.locker) 
     { 
      instance = this.GetInstance(); 
      this.referenceCount++; 
     } 

     instance.SomeOperation(); 
    } 

    public void Dispose() 
    { 
     IService instance = null; 

     lock (this.locker) 
     { 
      this.referenceCount--; 

      if (this.referenceCount == 0) 
      { 
       instance = this.wrappedService; 
       this.wrappedService = null; 
      } 
     } 

     // Dispose the object outside the lock for performance. 
     if (instance != null) 
     { 
      instance.Dispose(); 
     } 
    } 

    private IService GetInstance() 
    { 
     if (this.wrappedService == null) 
     { 
      this.wrappedService = this.factory(); 
     } 

     return this.wrappedService; 
    } 
} 

請注意,這個實現仍然是有缺陷的,因爲以下原因,:

  1. 調用Dispose多次打破了裝飾。
  2. 當消費者多次撥打SomeOperation(或IService有多個方法)時,實施將中斷。

很難創建一個按預期運行的裝飾器。這樣做的一個簡單方法是通過序列化訪問對象,但是當你這樣做時,你可能想要爲每個線程使用單個實例。這會容易得多。

我希望這會有所幫助。

+0

爲什麼你認爲IDisposable是一個漏洞抽象? (我不反對你,我從純粹的興趣問) – 2011-04-26 21:58:45

+0

@Johan:看看Mark Seemann的這些答案:http://stackoverflow.com/questions/2634675/ioc-containers-and-idisposable/2635733# 2635733和http://stackoverflow.com/questions/2404007/di-with-disposable-objects/2404104#2404104。 – Steven 2011-04-27 04:58:29

+1

謝謝!在SO上,Mark Seeman是IOC&DI Jon Skeet是C#的:)我特別喜歡*「......你有一個特定的實現,因此實現泄露到抽象中。」*在http:// stackoverflow.com/questions/2634675/ioc-containers-and-idisposable/2635733#2635733 – 2011-04-27 08:39:16

1

我知道這個問題已經解決了,但是...... @史蒂芬的回答並沒有指出Ninject中有一個InScope機制,它解決了你所尋找的問題。

看一看內特Kohari的Cache and Collect文章再怎麼範圍界定在Ninject 2

接下來要做,去看看ninject源,看看InRequestScope是如何實現(包括拆卸是如何連接的)。有一些工作計劃在2.3-4中概括如何工作,以便它可以用於一些複雜的託管方案。

當您查看這兩個參考資料時,請在ninject郵件列表上詢問一個問題,您一定會有解決方案。

相關問題