5.2.1.311
Important
This release changes how types are registered.
Bug fix
This release is a part of a fix of longest running Unity bug. The problem it addresses is with registrations affecting each other when new registration is the same Type as previously registered mapping.
Following code explains the problem:
IUnityContainer uc = new UnityContainer();
uc.RegisterType<ILogger, MockLogger>(new ContainerControlledLifetimeManager());
ILogger logger = uc.Resolve<ILogger>();
Assert.IsNotNull(logger);
Assert.AreSame(uc.Resolve<ILogger>(), logger); <-- Resolves Singleton as it should
uc.RegisterType<MockLogger>(new TransientLifetimeManager()); <-- Here is the problem
Assert.AreSame(uc.Resolve<ILogger>(), logger); <-- Resolves new MockLogger and fails
The issue is described here
Impact on existing code
To demonstrate impact on existing code please look at this example:
container.RegisterType<ISomething, Something>(new ContainerControlledLifetimeManager());
var a = container.Resolve<Something>();
var b = container.Resolve<ISomething>();
Assert.AreNotSame(a, b);
Due to the this bug resolving a and b returned the same result because ContainerControlledLifetimeManager
would be applied to ToType instead of FromType as it should. In other words it would be applied to Something
instead of ISomething
.
This fix corrects this behavior so only ISomething
registers with ContainerControlledLifetimeManager
and resolution of Something
type will use transient manager.
You could replicate behavior of Unity before this bug fix by simply registering mapped type with proper lifetime manager:
container.RegisterType<Something>(new ContainerControlledLifetimeManager());
For more information see: #35, 163, #164, #165, #170, #177
More strict use of InjectionFactory
This form of registration used to work for registering interface with InjectionFactory
but it is no longer allowed.
RegisterType<IService, Service>(new InjectionFactory(c => new Service()));
As written, this code registers both: a mapping between IService
and Service
and between IService
and InjectionFactory
. These two are mutually exclusive and create ambiguity which Unity can not resolve.
It should be either one of these but not both:
RegisterType<IService, Service>();
RegisterType<IService>(new InjectionFactory(c => new Service()));
Upgrading to Release v5.2.1
This release fundamentally changes how types are registered with Unity. The rationale behind this change is this issue.
The problem
To explain the problem please look at this example. Prior to this release registering singleton ILogger
service like this:
container.RegisterType<ILogger, MockLogger>(new ContainerControlledLifetimeManager(), new InjectionConstructor());
would create two registrations:
- A mapping between
ILogger
toMockLogger
- A singleton registration for
MockLogger
with default constructor.
Calling container.Resolve<ILogger>()
resolves singleton instance of MockLogger as expected, and resolving type MockLogger container.Resolve<MockLogger>()
would resolve the same instance of MockLogger. Both ContainerControlledLifetimeManager and InjectionConstructor would be associated with MockLogger registration.
Suppose you want to resolve a new MockLogger whenever it is resolved directly like this container.Resolve<MockLogger>()
. To do so you would create another registration just for the MockLogger:
container.RegisterType<MockLogger>(new TransientLifetimeManager());
So, now when you call container.Resolve<MockLogger>()
it resolves new instance of the MockLogger class and uses constructor with longest list of parameters. All is well and as expected. But now if you try to resolve container.Resolve<ILogger>()
it is no longer returns singleton instance of the MockLogger. Now it also returns new MockLogger created with constructor with longest list of parameters.
The subsequent registration overwritten all information associated with ILogger
.
The solution
Release 5.2.1 fixes this behavior. Now all information passed to Unity during registration is stored with FromType
instead of ToType
. So registering type like this:
container.RegisterType<ILogger, MockLogger>(new ContainerControlledLifetimeManager(), new InjectionConstructor());
creates just one registration ILogger
and associates LifetimeManager and all provided InjectionMemebers with it. At this point MockLogger is still unregistered.
So, think about it as a RegisteredType
and MappedTo
type. If you look at initial example:
container.RegisterType<ILogger, MockLogger>(new ContainerControlledLifetimeManager());
ILogger
- is a registered type and ContainerControlledLifetimeManager is associated with this type, as well as any InjectionMembers
you provide during registration.
Breaking changes
This release breaks a lot of registrations. Anything relaying on TypeTo being registered in mappings will fail. For example:
container.RegisterType<ILogger, MockLogger>(new ContainerControlledLifetimeManager());
Assert.AreSame( container.Resolve<ILogger>(), container.Resolve<MockLogger>()) <-- Will fail now
This could be easily fixed by slightly modifying how types are registered. If you want TypeTo to be available independently you could register it like this:
container.RegisterType<MockLogger>(new ContainerControlledLifetimeManager());
container.RegisterType<ILogger, MockLogger>();
Assert.AreSame( container.Resolve<ILogger>(), container.Resolve<MockLogger>()) <-- Passes
This applies to anything you registering with the type: factories, injection members, interceptors, etc.
Fixing
With some creative searching and sorting these breaking registrations could be identified statically, without running the code. The key is to look for registrations with same TypeTo type. If you see multiple registrations registering same type as implementation type and at least one of them has non transient lifetime it is a good indicator that it might fail after update:
container.RegisterType<ILogger, Logger>(new ContainerControlledLifetimeManager());
...
container.RegisterType<IOtherLogger, Logger>();
To fix just add individual registration for implementation type with proper lifetime manager like so:
container.RegisterType<Logger>(new ContainerControlledLifetimeManager());
...
container.RegisterType<ILogger, Logger>();
...
container.RegisterType<IOtherLogger, Logger>();
Make sure it is registered before other mappings.
Cleanup
Removed IDependencyResolverTrackerPolicy. The interface alone with its policy was invoked during registration and contributed to slowing down performance. Since it is not being used internally within Unity itself it was removed to speed up registration process.