1

我有以下代碼:我是否需要將synclock與使用單例作用域的DI容器管理的對象一起使用?

public class DotLessFactory 
{ 
    private LessEngine lessEngine; 

    public virtual ILessEngine GetEngine() 
    { 
     return lessEngine ?? (lessEngine = CreateEngine()); 
    } 

    private ILessEngine CreateEngine() 
    { 
     var configuration = new LessConfiguration(); 
     return new LessFactory().CreateEngine(configuration); 
    } 
} 

假設如下:

  1. 我總是想返回ILessEngine的同一個實例。
  2. 我使用DI容器(我將使用此示例中的StructureMap)來管理我的對象。
  3. 由於假設編號1,我的對象的生命週期將是Singleton。
  4. CreateEngine中的代碼執行通過NuGet包引用的代碼。
  5. DotLess僅僅是一個例子。此代碼可適用於任何類似的NuGet包。

方法1

註冊我的目標和我的DI容器使用:

For<DotLessFactory>().Singleton().Use<DotLessFactory>(); 
For<ILessEngine>().Singleton().Use(container => container.GetInstance<DotLessFactory>().GetEngine()); 
For<ISomeClass>().Singleton().Use<SomeClass>(); 

現在我可以ILessEngine添加到構造函數,有我的容器注入它的一個實例爲每碼下面。

public class SomeClass : ISomeClass 
{ 
    private ILessEngine lessEngine; 

    public SomeClass(ILessEngine lessEngine) 
    { 
     this.lessEngine = lessEngine; 
    } 
} 

方法2

介紹的IDotLessFactory接口暴露所述GetEngine方法。註冊我的目標和我的DI容器使用:

For<IDotLessFactory>().Singleton().Use<DotLessFactory>(); 
For<ISomeClass>().Singleton().Use<SomeClass>(); 

現在,我廠將創造ILessEngine實例按下面的代碼。

public class SomeClass : ISomeClass 
{ 
    private ILessEngine lessEngine; 

    public SomeClass(IDotLessFactory factory) 
    { 
     Factory = factory; 
    } 

    public IDotLessFactory Factory { get; private set; } 

    public ILessEngine LessEngine 
    { 
     get 
     { 
      return lessEngine ?? (lessEngine = Factory.GetEngine()); 
     } 
    } 
} 

我的問題是:

  1. 是什麼,當談到ILessEngine方法1和2之間的根本區別?在方法1中,ILessEngine由容器管理,在方法2中不是。每種方法的優缺點是什麼?一種方法比另一種更好嗎?
  2. 我是否需要在CreateEngine方法中使用synclock以確保任何方法的線程安全?什麼時候應該/不應該在這種情況下使用synclock?
  3. 我見過Activator.CreateInstance在CreateEngine方法內部使用的例子,而不是直接創建對象。有沒有人會使用這種方法的原因?有沒有在工廠對象中引入直接依賴關係到NuGet包內的對象?
  4. 我們假設被引用的NuGet包在HttpContext下工作。在單身作用域中註冊我的工廠對HttpContext有任何負面影響,或者這並不重要,因爲我認爲NuGet包很可能管理HttpContext本身的範圍?
  5. 最後,DotLessFactory最終將與Bundles(Microsoft.AspNet.Web.Optimization NuGet包)一起使用,並且Bundle僅在Application Start上實例化(不由容器管理)。 Bundle將取決於DotLessFactory的注入實例。這個事實對上述問題有什麼影響嗎?

任何反饋將非常有幫助。

回答

1

這是不平凡的具體回答這些問題,但讓我提供一些非詳盡的評論:

  1. 據我所知,沒有辦法的保障需求#1(單) 。這是因爲兩個線程可以同時執行查找,並將lessEngine評估爲null並觸發創建新實例。如果StructureMap查找是線程安全的,第一種方法可能最終會成爲線程安全的,但是如果出現這種情況,我會感到驚訝(不管怎樣,您不希望代碼依賴第三方庫中的實現「細節」 )。

  2. 這兩種解決方案都犯了同樣的錯誤,它主要是檢查一個實例是否已經創建,但沒有保護整個代碼區域。爲了解決這個問題,引入私人對象變量來鎖定和保護代碼區域創建實例:

    private object engineLock = new object(); 
    
    public virtual ILessEngine GetEngine() 
    { 
        lock(engineLock) { return lessEngine ?? (lessEngine = CreateEngine()); } 
    } 
    

    順便說一句,這不會是必要的,如果你可以讓StructureMap手柄建設整個對象鏈,因爲它會根據您的容器配置確定單體需求。

  3. 如果您知道它們具有默認構造函數(例如,通過類型參數的代碼中的通用約束),或者您有編譯時引用,則只能創建新對象。由於IoC主要創建它在編譯時不知道的東西,並且在這樣做時通常需要傳遞參數,所以使用Activator.CreateInstance。據我所知,使用「new」會生成IL來調用Activator.CreateInstance,所以最終結果都是一樣的。

  4. HttpContext的生命週期是由您的應用程序(由ASP.NET)管理的,因此不存在範圍問題。 HttpContext.Current將被設置或者不被設置,如果不是,那麼你在做的工作太早,因爲它是可用的(或者在它永遠不可用的上下文中執行,例如在ASP.NET之外) 。

  5. 呃,不知道你在考慮什麼潛在的問題,但我最好的猜測是它不應該對你的代碼有任何影響。

希望這有助於!

相關問題