我遇到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定义了公共构造函数(只是为了证明这个概念),一切都很顺利。
问题:
UPDATE:为什么BuildUp方法需要定义构造函数?
任何想法如何解决这个问题? (删除单身不是一种选择)
答案 0 :(得分:2)
如何为ContainerControlledLifetimeManager
使用SomeClass
(每个容器单身)。它不会是单例,但容器将始终返回相同的实例。
而不是SomeClass.Instance
,您将调用container.Resolve<SomeClass>()
答案 1 :(得分:1)
学习新东西永远不会太晚,经过两年多的努力,我准备回答自己的问题。
长话短说:这是一个known bug,它已在2.1.505.2中修复。详情如下。
该错误是在2010年9月针对Unity 2.0报告的,并且一直存在于框架中,直到2012年8月发布2.1.505.2。这解释了我们遇到它的原因,但不是为什么我们没有谷歌错误报告... < / p>
以下是重现此问题所需的代码。
单例类的定义(注意类根本不包含任何依赖项):
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此代码抛出了InvalidOperationException
或ResolutionFailedException
。从版本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及以下 - 有几种方法:
重构代码以摆脱单例,并在容器中注册具有适当生命周期的类型,如Ladislav Mrnka在此问题的另一个答案中所建议的那样。在我们的案例中,这是不可能的,但有时候可能还是可行的方式。
下载资源并重新编译,并更改上面发布的版本GuardTypeIsNonPrimitive
的实施。
实现自己的注入,例如作为UnityContainer
类的扩展方法。可以找到一个示例here(链接本身已经死了,因此链接Web Archive版本)。
一如既往,正确的选择取决于具体情况。