Stack overflow in Unity DI Container when registering a Decorator

After upgrading to a newer version of Unity, we found that our process began to fail with StackOverflowException. Investigation showed that it happened because the following registration behaves differently depending on the version of Unity:

container.RegisterType<TInterface, TType>(new InjectionFactory(...));

Github project ikriv/UnityDecoratorBug explores the behavior for different versions. Strictly speaking, the registration is self-contradictory: it may mean either

// (1) Resolve TInterface to whatever is returned by the InjectionFactory
container.RegisterType<TInterface>(new InjectionFactory(...));


// (2) Resolve TInterface to container-built instance of TType
container.RegisterType<TInterface, TType>();

Early versions of Unity ignored the TType part and used interpretation #1. In the release notes for version 5.2.1 it was declared that such registrations are no longer allowed, but in reality they were silently interpreted as (2), ignoring the InjectionFactory part.

This led to a catastrophic problem in our code. We had a decorator type similar to this:

public class Decorator : IService
    public Decorator(IService service) {...}

and the registration declaration was

container.RegisterType<IService, Decorator>(
new InjectionFactory(c => new Decorator(c.Resolve<ConcreteService>())));

If the injection factory is ignored, this leads to an infinite recursion when resolving IService: IService is resolved to Decorator, that accepts IService as a parameter, that is resolved to Decorator, that needs IService as a paramter, that is resolved to Decorator

In Unity version 5.7.0 this was fixed and such registrations throw an InvalidOperationException.

To recap:

Unity version Effect of the registration
<=5.2.0 Injection factory used
5.2.1 to 5.6.1 Injection factory ignored, StackOverflowException for decorators
>=5.7.0 InvalidOperationException

Lessons learnt:

  • If something is not allowed, it must throw an exception
  • Silently changing interpretation of existing code is evil.

PS. This is probably the first time when actually lived to its name: usually I search for other kinds of exceptions there. 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *