Ninject 4.0.0-beta-0134 抛出“在两个服务的构造函数之间检测到循环依赖”

问题描述

今天我将我们的 Ninject 依赖从 3.3.4 更新到 4.0.0-beta-0134,但现在它在装饰器模式中抛出了一个循环依赖异常:

`未处理的异常:Ninject.ActivationException:使用从 Program+IService 到 Program+Service 的条件绑定激活 Program+IService 时出错 在两个服务的构造函数之间检测到循环依赖。

激活路径: 2) 将依赖 Program+IService 注入到 Program+ServiceDecorator 类型的构造函数的参数服务中

  1. 请求 Program+IService

建议:

  1. 确保您没有在服务的任何实现上声明 Program+IService 的依赖项。
  2. 考虑将这些服务合并为一个服务以消除循环。
  3. 使用属性注入代替构造函数注入,并实现IInitializable 如果您需要在注入属性值后运行初始化逻辑。 `

这是示例代码

    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();
        }
    }

它似乎不喜欢在 NinjectModule 中使用 WhenInjectedInto。我发现了其他类似的问题,但没有发现 WhenInjectedInto 不起作用。

解决此问题的一种方法是将服务参数类型从 IService 更改为 Service,但这会破坏使用装饰器的大部分原因。

你们中有人知道其他解决方案/变通方法吗?

解决方法

不清楚您打算如何使用此示例中的装饰器,但一种可能性是对装饰器应用一个新接口,该接口又继承自 IService

public interface IServiceDecorator : IService { }

public interface IService
{
    void Serve();
}

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

public class ServiceDecorator : IServiceDecorator
{
    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>().InSingletonScope();
        Bind<IService>().To<Service>().WhenInjectedInto<ServiceDecorator>().InSingletonScope();
        Bind<IServiceDecorator>().To<ServiceDecorator>().InSingletonScope();
    }
}
,

再次通过后,似乎问题实际上是由使用 .WhenInjectedInto<ServiceDecorator> 引起的,因为绑定之间没有区别。

另一种方法是简单地使用 Bind<IService>().To<Service>().InSingletonScope();,然后将 BindingConfiguration.Condition 添加到您的装饰器来处理 Get<ServiceDecorator>() 用例:

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);
    }
}