Search code examples
c#ninject

Ninject 4.0.0-beta-0134 throws 'A cyclical dependency was detected between the constructors of two services'


Today I updated our Ninject dependency from 3.3.4 to 4.0.0-beta-0134, but now it throws a circular dependency exception in decorator pattern:

`Unhandled Exception: Ninject.ActivationException: Error activating Program+IService using conditional binding from Program+IService to Program+Service A cyclical dependency was detected between the constructors of two services.

Activation path: 2) Injection of dependency Program+IService into parameter service of constructor of type Program+ServiceDecorator

  1. Request for Program+IService

Suggestions:

  1. Ensure that you have not declared a dependency for Program+IService on any implementations of the service.
  2. Consider combining the services into a single one to remove the cycle.
  3. Use property injection instead of constructor injection, and implement IInitializable if you need initialization logic to be run after property values have been injected. `

Here is sample code:

    public static void Main(string[] args)
    {
        var kernel = new StandardKernel(); 
        kernel.Load(new IocConfig());
        kernel.Get<IService>().Serve();
    }

    internal interface IService
    {
        void Serve();            
    }

    public class Service : IService
    {
        public void Serve() { }
    }

    public class ServiceDecorator : IService
    {
        private readonly IService _service;

        public ServiceDecorator(IService service) => _service = service;

        public void Serve() => _service.Serve();
    }

    public class IocConfig : NinjectModule
    {
        public override void Load()
        {
            Bind<IService>().To<Service>().WhenInjectedInto<ServiceDecorator>().InSingletonScope();
            Bind<IService>().To<ServiceDecorator>().InSingletonScope();
        }
    }

It seams like it does not like the use of WhenInjectedInto in the NinjectModule. I found other similar questions, but none where WhenInjectedInto was not working.

One way to fix this is to change the service parameter type from IService to Service but that destroys much of the reason for using the Decorator.

Do any of you know of another solution/workaround?


Solution

  • After making another pass, it would appear that the issue is actually caused by the use of .WhenInjectedInto<ServiceDecorator> since there is no differentiation between the bindings.

    An alternative is to simply use Bind<IService>().To<Service>().InSingletonScope(); and then add a BindingConfiguration.Condition to your decorator to handle the Get<ServiceDecorator>() use case:

    public static void Main(string[] args)
    {
        var kernel = new StandardKernel();
        kernel.Load(new IocConfig());
        kernel.Get<IService>().Serve();
        kernel.Get<ServiceDecorator>().Serve();
    }
    
    public interface IService
    {
        void Serve();
    }
    
    public class Service : IService
    {
        public void Serve() { Console.WriteLine("service"); }
    }
    
    public class ServiceDecorator : IService
    {
        private readonly IService _service;
    
        public ServiceDecorator(IService service) => _service = service;
    
        public void Serve() { Console.WriteLine("decorator"); _service.Serve(); }
    }
    
    public class IocConfig : NinjectModule
    {
        public override void Load()
        {
            Bind<IService>().To<Service>().InSingletonScope();
            Bind<IService>().To<ServiceDecorator>().InSingletonScope().BindingConfiguration.Condition =
                (Ninject.Activation.IRequest request) =>
                    request.Service == typeof(ServiceDecorator);
        }
    }