Unity BuildUp fails for singleton
I am experiencing the following problem with Unity framework.
We have a singleton classes in our project. They have some properties that should be injected by Unity container. Here is the code:
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;
}
}
This code fails with the following exception: The type SomeClass cannot be constructed. You must configure the container to supply this value.
I've digged into Unity code and found out that the problem was caused by method PreBuildUp, which calls GuardTypeIsNonPrimitive, both defined in Microsoft.Practices.ObjectBuilder2.DynamicMethodConstructorStrategy class. Here is the piece of it's code:
public override void PreBuildUp(IBuilderContext context)
{
...
SelectedConstructor selectedConstructor = context.Policies.Get<IConstructorSelectorPolicy>(context.BuildKey, out list).SelectConstructor(context, list);
开发者_如何学JAVA 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 }));
}
}
As we can see, Unity tries to find constructor for the class that should be built up. Since the only constructor defined for SomeClass is private, Unity finds nothing and passes null to GuardTypeIsNonPrimitive. And this method throws an exception. At the moment I have defined public constructor for SomeClass (just to prove the concept), everything worked out fine.
Questions:
UPDATE: Why does the BuildUp method requires constructor to be defined?
Any ideas how to work this around? (Remove singleton is not an option)
What about using ContainerControlledLifetimeManager
(per container singleton) for your SomeClass
. It will not be singleton as is but the container will return always the same instance.
Instead of SomeClass.Instance
you will call container.Resolve<SomeClass>()
It is never too late to learn something new, and after more than two years I am ready to answer my own question.
Long story short: this is a known bug, and it was fixed in 2.1.505.2. Details below.
The bug was reported on September 2010 for Unity 2.0, and lived in the framework until 2.1.505.2 was released in August 2012. This explains why we encountered it, but not why we failed to google the bug report...
Here is the code that is needed to reproduce the issue.
Definition of a singleton class (note that class does not contain any dependencies at all):
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;
}
}
}
Actual BuildUp
call:
UnityContainer container = new UnityContainer();
SingletonClass singleton = container.BuildUp(SingletonClass.Instance);
Up to version 2.1.505.0 this code threw either InvalidOperationException
or ResolutionFailedException
. Starting from version 2.1.505.2 this code works fine (as it should by design, from my point of view).
Interesting to know that the actual fix was done by rewriting the piece of code I have outlined in the question. Here is how the corresponding parts of Microsoft.Practices.ObjectBuilder2.DynamicMethodConstructorStrategy
now look:
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));
}
}
}
Most important part here is that now guard method GuardTypeIsNonPrimitive
does not take into account constructor - just the type itself. I think that was the root of the problem.
Fact that this is a bug answers the first question in the post. What about the second? How to work around this issue? If you are using Unity 2.1.505.2 and above you do not have that issue, so the option most welcomed is to update your Unity version. If however you have to deal with 2.1.505.0 and below - there are several approaches:
Refactor code to get rid of the singleton, and register type in the container with the appropriate lifetime, as suggested by Ladislav Mrnka in another answer to this question. It was not possible in our case, but still sometimes it might be the way to go.
Download sources and recompile them, changing implementation of the
GuardTypeIsNonPrimitive
with version posted above.Implement your own injection, for example as an extension method of the
UnityContainer
class. One example can be found here (the link itself is dead, so linking the Web Archive version instead).
As always right choice depends on the particular situation.
精彩评论