stop-cran
stop-cran

Reputation: 4408

Unity container - wrong registration is applied when mapped two interfaces on the same class

I have a class which implements two interfaces:

interface IA { int X { get; } }
interface IB { int X { get; } }

class C : IA, IB
{
    public C(int x) { X = x; }
    public int X { get; }
}

Then I map IA and IB on C in a Unity container and check that a correct registration is used:

var container = new UnityContainer();

container
    .RegisterType<IA, C>(new InjectionConstructor(1))
    .RegisterType<IB, C>(new InjectionConstructor(2));

var a = container.Resolve<IA>();

a.X.ShouldBe(1); // Actually it's 2

Here I'm getting a.X equals 2 instead of 1. Hence the second registration is used instead of the first one.

Who can explain, why above registrations interfer each other? Is there any sane reason for it?

Upvotes: 1

Views: 66

Answers (1)

Randy Levy
Randy Levy

Reputation: 22655

From a (simplified) high level Unity stores two pieces of information that are used when resolving:

  1. Type Mapping
  2. Build Plan

The key point to keep in mind is that type mapping and the building of the target type are separate discrete steps.

Type mapping stores the mapping between the "from" type and the "to" type. So, in the sample code IA=>C and IB=>C.

The build plan contains the steps required to construct the "to" target type. In this case that type is C.

So when

.RegisterType<IA, C>(new InjectionConstructor(1))

is called a type mapping is created from IA to C and the build plan strategies associated with the target type C are created and saved in memory. The build plan strategies would include the default lifetime manager and the InjectionConstructor with a value of 1.

Next when

.RegisterType<IB, C>(new InjectionConstructor(2));

is called a type mapping is created from IB to C and the build plan strategies associated with the target type C are updated in memory. This would change the build plan strategies to use the InjectionConstructor(2) instead of InjectionConstructor(1).

Now there are two type mappings defined (IA=C and IB=>C) but still only one build plan for C. That is why the resolve returns a value of 2 instead of 1. (You could verify this by resolving C directly and checking the value of X: container.Resolve<C>()).

If you want to have multiple build plans for the same target type then you could use named registrations. For example you could register using the name of the from interface (or any other string value you wanted but the name of the interface seems apropos here):

var container = new UnityContainer();

container
    .RegisterType<IA, C>(typeof(IA).Name, new InjectionConstructor(1))
    .RegisterType<IB, C>(typeof(IB).Name, new InjectionConstructor(2));

var a = container.Resolve<IA>(typeof(IA).Name);

a.X.ShouldBe(1); // will be 1

Upvotes: 1

Related Questions