2011-05-13 71 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