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

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

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

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

激活路径: 2) Program+ServiceDecorator

类型构造函数的参数服务注入依赖Program+IService
  1. 请求程序+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();
        }
    }

它似乎不喜欢在 Ninject 模块中使用 WhenInjectedInto。我发现了其他类似的问题,但是 none 其中 WhenInjectedInto 不起作用。

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

你们知道另一个 solution/workaround 吗?

不清楚您打算如何使用此示例中的装饰器,但一种可能是对装饰器应用一个新接口,该接口又继承自 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);
    }
}