Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unity behavior for registering the same type twice and resolving only the singleton

I've been playing around with Unity to understand it a bit more, and came across the following scenario. If I was to register the same type twice, but with one being a singleton, how can I call Resolve so that only the singleton is returned? I understand that this can be accomplished using a unique Name, but am wondering if this can be done without.

For example:

_container.RegisterType<Music>(new InjectionConstructor(new Album("Non-singleton", "Non-singleton")));
_container.RegisterType<Music>(new ContainerControlledLifetimeManager());

and calling

var music = Factory.Resolve<Music>();

returns the 'Non-singleton' object. At first, I thought it was based on registration order, but switching that around I still receive the 'Non-singleton' instance.

Is there a reason for this? Is there a way to Resolve only the singleton type for two registered objects in the same container w/out specifying a Name attribute?

like image 522
d.moncada Avatar asked Aug 16 '13 19:08

d.moncada


1 Answers

Let's break it down:

_container.RegisterType<Music>(new InjectionConstructor(
    new Album("Non-singleton", "Non-singleton")));

This registers with the container the type Music with no name (null or default) and tells Unity to use the constructor that accepts a type Album. Also to always pass the instance created by new Album("Non-singleton", "Non-singleton") as the value to the Music(Album album) constructor. So every Music instance will have the same Album injected into it.

_container.RegisterType<Music>(new ContainerControlledLifetimeManager());

This performs a registration based on the type Music with no name (null or default). Because, there are no InjectionMembers specified the only change to the existing registration is to add a lifetime manager. The existing registration is updated because the BuildKey (Type: Music, Name: null) is the same for both RegisterType calls.

unityContainer.RegisterType<Music>(new InjectionConstructor(new Album("Non-singleton", "Non-singleton")));

unityContainer.RegisterType<Music>(new ContainerControlledLifetimeManager());

var music = unityContainer.Resolve<Music>();
var music2 = unityContainer.Resolve<Music>();

bool areEqual = ReferenceEquals(music, music2);

In the above areEqual is true, so a single instance is being returned.

Is there a way to Resolve only the singleton type for two registered objects in the same container w/out specifying a Name attribute?

Types are registered by Type and Name so the only way to have multiple registrations for the same type is to use a name:

// Named registration
unityContainer.RegisterType<Music>("NonSingleton Name",
    new InjectionConstructor(new Album("Non-singleton", "Non-singleton")));

// Default (null) registration
unityContainer.RegisterType<Music>(new ContainerControlledLifetimeManager());

Now if you wanted to override the first registration you could do that by specifying a new InjectionConstructor. This would clear the build plan and set the constructor selection policy.

unityContainer.RegisterType<Music>(new InjectionConstructor(
    new Album("Non-singleton", "Non-singleton")));

unityContainer.RegisterType<Music>(new ContainerControlledLifetimeManager(),
    new InjectionConstructor(new Album("singleton", "singleton")));

As a side note, you would usually not hard code the dependency (in this case Album) inside of the register type call unless it was truly a constant value. Even then you might want to register the constant instance as a singleton and let the container resolve it for you.

An interesting question is how would you reset the constructor selection policy after the first RegisterType call so that Unity would use its default constructor selection policy. You could do this with a custom InjectionMember that clears the constructor selection policy:

unityContainer.RegisterType<Music>(new InjectionConstructor(
    new Album("Non-singleton", "Non-singleton")));

unityContainer.RegisterType<Music>(new ContainerControlledLifetimeManager(),
    new ClearInjectionConstructor());

public class ClearInjectionConstructor : InjectionMember
{
    public override void AddPolicies(Type serviceType, 
        Type implementationType, 
        string name, 
        Microsoft.Practices.ObjectBuilder2.IPolicyList policies)
    {
        policies.Clear<IConstructorSelectorPolicy>(
            new NamedTypeBuildKey(implementationType, name));
    }
}
like image 152
Randy supports Monica Avatar answered Oct 18 '22 04:10

Randy supports Monica



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!