Search code examples
c#dependency-injectionninjectstack-overflow

Stack overflow when resolving services with Ninject


I'm trying to resolve a known fixed list of Foo services (in singleton scope) using NInject; this resolution happens in a FooProvider's constructor. The problem is that each Foo will need this provider as well.

public interface IFoo { }
public interface IFooProvider { }

public class Foo : IFoo
{
    private readonly IFooProvider _provider;

    public Foo(IFooProvider provider)
    {
        _provider = provider;
    }
}

public class FooProvider : IFooProvider
{
    private List<IFoo> _allFooServices;

    public FooProvider(IKernel kernel)
    {
        _allFooServices = kernel.GetAll<IFoo>().ToList();
    }
}

public class Program
{
    private static void Main(string[] args)
    {
        var IoC = new StandardKernel();

        IoC.Bind<IFoo>().To<Foo>().InSingletonScope();
        IoC.Bind<IFooProvider>().To<FooProvider>().InSingletonScope();

        var foo = IoC.Get<IFoo>();
    }
}

There is a logical cyclic loop here, and obviously the stack overflow shows its going down it. However, I have both interface bound to singletons.

Thinking about it; we try to resolve IFoo, which then needs resolution of IFooProvider, itself needing a list of IFoo... but we haven't resolved for any IFoo singletons yet because we're still trying to resolve it!

So how could I work around this?

[Edit] Possible solution; delay buffering of IFoo service instances.

public FooProvider(IKernel kernel)
{
    _kernel = kernel;
}

public IFoo Find(object context)
{
    if (_allFooServices == null)
        _allFooServices = _kernel.GetAll<IFoo>().ToList();

    return _allFooServices.Where(...

[Why?]

The general idea is to avoid the service locator pattern, as I've seen it described as an anti-pattern. So, rather than try to resolve services through the dependency injector during run time; you try to get a list of services during setup. The problem with this though, is that if any of your services want to find other services, you have the above problem.


Solution

  • You cannot resolve this cyclic dependency with Ninject. You are not even able to create such object graph by hand.

    First you have to remove the cyclic dependency from at least one constructor. You can move this dependency to property and use property injection.

    public class Foo : IFoo
    {
       [Inject]
       public IFooProvider Provider { get; set; }
    }
    

    If you want to avoid service-locator pattern you should remove dependency on IKernel from the constructor of FooProvider and use the injection of collection of registered IFoo implementations instead.

    public class FooProvider : IFooProvider
    {
        private List<IFoo> _allFooServices;
    
        public FooProvider(IEnumerable<IFoo> fooServices)
        {
            _allFooServices = fooServices.ToList();
        }
    }