1

我在使用 Unity 框架时遇到以下问题。

我们的项目中有一个单例类。它们有一些属性应该由 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。您必须配置容器以提供此值。

我深入研究了 Unity 代码,发现问题是由方法 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. 更新:为什么 BuildUp 方法需要定义构造函数?

  2. 任何想法如何解决这个问题?(删除单例不是一种选择)

4

2 回答 2

2

ContainerControlledLifetimeManager为您的SomeClass. 它不会是单例,但容器将始终返回相同的实例。

而不是SomeClass.Instance你会打电话container.Resolve<SomeClass>()

于 2011-05-13T10:59:10.847 回答
1

学习新东西永远不会太晚,两年多后,我准备回答我自己的问题。

长话短说:这是一个已知错误,已在 2.1.505.2 中修复。详情如下。

该错误是在 2010 年 9 月针对 Unity 2.0 报告的,并且一直存在于框架中,直到 2012 年 8 月发布 2.1.505.2。这解释了我们遇到它的原因,但不能解释为什么我们无法通过谷歌搜索错误报告...

这是重现问题所需的代码。

单例类的定义(注意该类根本不包含任何依赖项):

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.DynamicMethodConstructorStrategynow 对应部分的外观:

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));
        }
    }
}

这里最重要的部分是 now guard 方法GuardTypeIsNonPrimitive不考虑构造函数——只考虑类型本身。我认为这是问题的根源。

事实上,这是一个错误回答了帖子中的第一个问题。第二个呢?如何解决这个问题?如果您使用的是 Unity 2.1.505.2 及更高版本,则没有该问题,因此最受欢迎的选项是更新您的 Unity 版本。但是,如果您必须处理 2.1.505.0 及以下版本 - 有几种方法:

  1. 正如Ladislav Mrnka在对这个问题的另一个回答中所建议的那样,重构代码以摆脱单例,并在具有适当生命周期的容器中注册类型。在我们的情况下这是不可能的,但有时它仍然可能是要走的路。

  2. 下载源代码并重新编译它们,更改GuardTypeIsNonPrimitive上面发布的版本的实现。

  3. 实现自己的注入,例如作为UnityContainer类的扩展方法。可以在此处找到一个示例(链接本身已失效,因此请改为链接 Web Archive 版本)。

一如既往,正确的选择取决于具体情况。

于 2013-06-01T17:34:53.690 回答