2011-05-13 72 views
1

我遇到以下問題與Unity框架。統一BuildUp失敗的單身人士

我們在我們的項目中有一個singleton類。他們有一些屬性應該由Unity容器注入。下面是代碼:

private static SomeClass m_Instance; 

private SomeClass() 
{ } 

public static SomeClass Instance 
{ 
    get 
    { 
     if (m_Instance == null) 
     { 
      lock (typeof(SomeClass)) 
      { 
       if (m_Instance == null) 
       { 
        IUnityContainer container = ContainerAccessor.GetContainer(); 
        m_Instance = container.BuildUp<SomeClass>(new SomeClass()); 
       } 
      } 
     } 

     return m_Instance; 
    } 
} 

此代碼失敗,出現以下異常:類型SomeClass的無法構造。您必須配置容器以提供此值。

我已經挖成統一的代碼並發現問題是由方法PreBuildUp,它調用GuardTypeIsNonPrimitive,無論是在Microsoft.Practices.ObjectBuilder2.DynamicMethodConstructorStrategy類中定義引起的。這是它的代碼片段:

public override void PreBuildUp(IBuilderContext context) 
{ 
    ... 
    SelectedConstructor selectedConstructor = context.Policies.Get<IConstructorSelectorPolicy>(context.BuildKey, out list).SelectConstructor(context, list); 
    GuardTypeIsNonPrimitive(context, selectedConstructor); 
    ... 
} 

private static void GuardTypeIsNonPrimitive(IBuilderContext context, SelectedConstructor selectedConstructor) 
{ 
    Type type = context.BuildKey.Type; 
    if (!type.IsInterface && ((type == typeof(string)) || (selectedConstructor == null))) 
    { 
     throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.TypeIsNotConstructable, new object[] { type.Name })); 
    } 
} 

正如我們所看到的,Unity試圖找到應構建的類的構造函數。由於爲SomeClass定義的唯一構造函數是私有的,因此Unity找不到任何內容,並將null傳遞給GuardTypeIsNonPrimitive。而這種方法會引發異常。目前我已經定義了SomeClass的公共構造函數(只是爲了證明這個概念),一切都很好。

問題:

  1. UPDATE:爲什麼要定義方法需要構造的積累?

  2. 任何想法如何解決這個問題? (刪除單是不是一種選擇)

回答

1

這是永遠不會太遲學習新的東西,經過兩年多的我願意回答我的問題。

長話短說:這是一個known bug,它固定在2.1.505.2。下面的細節。

2010年9月發佈了Unity 2.0的bug,並且一直住在框架中,直到2012年8月發佈2.1.505.2。這就解釋了爲什麼我們遇到了這個問題,但並不是爲什麼我們沒有谷歌bug報告......

以下是重現問題所需的代碼。一個單獨的類的

定義(注意,在類的所有不包含任何依賴關係):

public class SingletonClass 
{ 
    private static SingletonClass m_Instance; 

    private SingletonClass() 
    { 
    } 

    public static SingletonClass Instance 
    { 
     get 
     { 
      if (m_Instance == null) 
      { 
       m_Instance = new SingletonClass(); 
      } 

      return m_Instance; 
     } 
    } 
} 

實際BuildUp電話:

UnityContainer container = new UnityContainer(); 
SingletonClass singleton = container.BuildUp(SingletonClass.Instance); 

最高版本爲2.1.505.0這段代碼要麼扔InvalidOperationExceptionResolutionFailedException。從版本2.1.505.2開始,這段代碼可以正常工作(從我的角度來看,它應該是設計的)。

有趣的是,通過重寫我在問題中概述的代碼段來完成實際的修復。這裏是Microsoft.Practices.ObjectBuilder2.DynamicMethodConstructorStrategy相應部位如何現在看起來:

public override void PreBuildUp(IBuilderContext context) 
{ 
    ... 
    SelectedConstructor selectedCtor = selector.SelectConstructor(context, resolverPolicyDestination); 

    GuardTypeIsNonPrimitive(context); 
    ... 
} 

private static void GuardTypeIsNonPrimitive(IBuilderContext context) 
{ 
    var typeToBuild = context.BuildKey.Type; 
    if (!typeToBuild.GetTypeInfo().IsInterface) 
    { 
     if (typeToBuild == typeof(string)) 
     { 
      throw new InvalidOperationException(
       string.Format(
        CultureInfo.CurrentCulture, 
        Resources.TypeIsNotConstructable, 
        typeToBuild.GetTypeInfo().Name)); 
     } 
    } 
} 

這裏最重要的是,現在防範方法GuardTypeIsNonPrimitive不考慮構造 - 只是類型本身。我認爲這是問題的根源。

事實上,這是一個錯誤回答了帖子中的第一個問題。第二個呢?如何解決這個問題?如果您使用的是Unity 2.1.505.2或更高版本,則不存在該問題,因此最受歡迎的選項是更新Unity版本。然而,如果你要處理2.1.505.0及以下 - 有幾種方法:

  1. 重構代碼擺脫單身的,並與適當的壽命容器註冊類型,由Ladislav Mrnka的建議這個問題的另一個答案。在我們的情況下是不可能的,但有時候它可能是一條路。

  2. 下載源代碼並重新編譯它們,改變GuardTypeIsNonPrimitive的實現與上面發佈的版本。

  3. 執行自己的注射,例如作爲UnityContainer類的擴展方法。一個例子可以找到here(鏈接本身已經死了,所以改爲鏈接Web Archive版本)。

一如既往的正確選擇取決於特定的情況。

2

怎麼樣使用ContainerControlledLifetimeManager(每個集裝箱單)爲您SomeClass。它不會是單例,但容器將始終返回相同的實例。

相反的SomeClass.Instance你會打電話container.Resolve<SomeClass>()

+0

+1是的,使用容器來保存你的單身人士。這是你擁有容器的原因之一。 – PVitt 2011-05-13 11:03:00

+0

如果我錯了,請糾正我,但您的建議似乎認爲SomeClass已在容器中註冊。但它不是,也不會是,這就是我們在這裏試圖使用BuildUp方法的原因。 – Andrei 2011-05-13 11:04:21

+0

然後你必須提供一個容器的構造函數。 – PVitt 2011-05-13 11:09:45