Singleton dependency resolved too early?

Jun 13, 2013 at 11:24 AM
Edited Jun 13, 2013 at 11:24 AM
Consider a scenario where there is one service and two implementations with one implementation being a factory proxy that takes an Func<IService>. The other implementation requires an IDependency which is registered as singleton.

The singleton is resolved as soon as an instance of IService is requested, although that correctly returns the proxy and so, in fact, an instance of IDependency is not yet needed.

A simple example demonstrates this:
var container = new Container();
container.Register<IService, Service>();
container.RegisterDecorator(typeof(IService), typeof(ServiceFactoryProxy));
container.RegisterSingle<IDependency>(
    () => { throw new InvalidOperationException("IDependency requested"); });

container.GetInstance<IService>(); // Should work but triggers the exception
Types used in this snippet:
public interface IService
{
}

public class Service : IService
{
    public Service(IDependency dependency)
    {
    }
}

public class ServiceFactoryProxy : IService
{
    public ServiceFactoryProxy(Func<IService> factory)
    {
    }
}

public interface IDependency
{
}
Is this a bug or is this intended?
Coordinator
Jun 13, 2013 at 9:01 PM
This is by design. During the time the ServiceFactoryProxy class is resolved the container needs to compile the Func<IService> delegate. As a performance optimization, Simple Injector hard wires singletons into the compiled delegates (using Expression.Constant under the covers). This means that at that time of the delegate compilation, that single instance need to be created.

In other words, the object graph looks like this:
Func<IService> factory = () => new Service(dependency)
// 
This would equal:
Func<IService> factory =
    Expression.Lambda<Func<IService>>(
        Expression.New(
            typeof(Service).GetConstructors().Single(), 
            Expression.Constant(dependency)))
        .Compile();
Notice that there is no callback to the container when resolving the sub element 'dependency'.

You can see that your problem goes a way when you register the dependency as anything else than singleton:
container.Register<IDependency>(
    () => { throw new InvalidOperationException("IDependency requested"); });
Or when you register the dependency as a Lazy<T>:
container.RegisterSingle<Lazy<IDependency>>(
    new Lazy<IDependency>(() => { throw new InvalidOperationException("IDependency requested"); }));
This however means that you need to change Service to depend on Lazy<IDependency> instead, but this is not encouraged, since you complicate the applications with things that the application should not be concerned with. Instead of registering a Lazy<T>, I generally advice to register a proxy class (that itself might accept a Lazy<IDependency>), but I'm not sure that's what you need in this case.

I would say that usually what you see is good, since it allows you to verify that the object graph is ok.
Marked as answer by dot_NET_Junkie on 2/26/2014 at 1:35 PM
Jun 13, 2013 at 9:06 PM
Edited Jun 13, 2013 at 9:53 PM
Thanks for the feedback. The "inlining" using Expression.Constant makes perfect sense.
While trying to provide a reproduction I already noticed myself that this only happened for singletons, hence my post here in Discussions and not in Issues.

I thought about using Lazy<T>, too, and discarded that idea for the very same reasons you stated.
The next thing I thought about was a proxy that depends on a Lazy<T>, just as you said. But I, too, am unsure whether that is what I really want. I will start a new discussion for my concrete scenario.

Thanks