【问题标题】:Cyclic dependency with ninjectninject 的循环依赖
【发布时间】:2010-08-18 11:29:07
【问题描述】:

我正在尝试找出正确的方法如何将这样的东西与 ninject 绑定。​​

interface IMainService
{
    void DoStuff();
}

interface IOtherService
{
    void DoSomeMagic();
}

abstract class BaseClass
{
    //many stuff here
}

class MainClass : BaseClass, IMainService
{
    public MainClass(IOtherService s)
    {
    }

    public void DoStuff()
    {
        throw new NotImplementedException();
    }

    //do many other things
}

class OtherClass : IOtherService
{
    public OtherClass(IMainService s)
    {
    }

    public void DoSomeMagic()
    {
        throw new NotImplementedException();
    }
}

class BaseModule : NinjectModule
{
    public override void Load()
    {
        Bind<MainClass>().To<MainClass>();
        Bind<IMainService>().To<MainClass>();
        Bind<IOtherService>().To<OtherClass>();
    }
}

static class Program
{
    static void Main()
    {
        var kernel = new StandardKernel(new BaseModule());
        var main = kernel.Get<MainClass>();
    }
}

它给了我例外:

Error activating IOtherService using binding from IOtherService to OtherClass
A cyclical dependency was detected between the constructors of two services.

Activation path:
  4) Injection of dependency IOtherService into parameter s of constructor of type MainClass
  3) Injection of dependency IMainService into parameter s of constructor of type OtherClass
  2) Injection of dependency IOtherService into parameter s of constructor of type MainClass
  1) Request for MainClass

Suggestions:
  1) Ensure that you have not declared a dependency for IOtherService 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.

我不知道如何编写 BaseModule。我只需要一个 MainClass 实例和一个 OtherClass 实例(比如单例)。

我尝试过这样的事情:

Bind<MainClass>().To<MainClass>().InSingletonScope();
Bind<IMainService>().To<MainClass>().InRequestScope();
Bind<IOtherService>().To<OtherClass>().InSingletonScope();

但同样的错误。

以及如何编写绑定只使用 MainClass 和 IMainService 接口的一个实例?

感谢您的回答。

【问题讨论】:

    标签: c# dependency-injection inversion-of-control ninject


    【解决方案1】:

    正如错误消息所说,MainClassOtherClass 之间存在循环依赖关系,因为如果没有另一个实例,就无法创建一个。理想情况下,您应该重新构建类层次结构以消除此要求。

    如果不能,解决方案是对一个(或两个)类使用属性注入,例如

    public interface IMainService
    {
        void DoStuff();
        IOtherService OtherService { set; }
    }
    
    public class MainClass
    {
        public IOtherService OtherService { get; set; }
        public void DoStuff() { ... }
    }
    
    public class OtherService
    {
        public OtherService(IMainService main)
        {
            main.OtherService = this;
        }
    }
    

    【讨论】:

    • 感谢您的提示。我找到了属性注入的完美解决方案。但它没有 IOtherService OtherService { set;在 IMainServices 上,因为当我用 [Inject] 装饰属性时,Ninject 会自行为其添加正确的实例。
    • 这不起作用。使用最新版本的 Ninject,如果您对两者都使用属性注入,它将抛出 StackOverflowException,如果只有一个使用属性注入(和另一个构造函数注入),它将抛出“检测到循环依赖”。
    • 啊,但它确实工作,只要您使用的范围不是瞬态范围(默认)。
    【解决方案2】:

    我认为你不应该为此使用属性或 setter 方法,你最好使用 Lazyness。懒惰的概念解决了这个问题。问题在于,如果对象之间存在循环依赖关系,则不清楚首先要创建什么。懒惰的解决方法是:一旦真正使用了对象(通常是调用公共方法时的情况,它需要存在)。如果可能,请避免使用属性或设置器。它们使您的对象可变(不利于线程安全,并且当依赖项应该只注入一次时不需要)。

    你的构造函数应该是这样的:

    public OtherService(Lazy<IMainService> main)
    {
        this.main = main;
    }
    
    public MainClass(Lazy<IOtherService> s)
    {
        this.s = s;
    }
    

    你可以在你的 Ninject 模块中使用 Load 方法来描述这些惰性依赖,方法是调用“ToMethod(”lambda method that created the Lazy method based on the get method”)。

    这里提供了一个关于惰性如何使用 Ninject 解决循环依赖的清晰示例。它还描述了一个帮助方法 (BindLazy) 来解决您的问题。 https://www.codeproject.com/Tips/1171940/How-Ninject-Can-Help-in-Resolving-Circular-Depende

    【讨论】:

    • 我用过这个。这很整洁!
    • 感谢您指出这可以使用Lazy&lt;T&gt; 来完成;这是个好主意。然而,特别是对于 Ninject,我应该指出,有一些现成的扩展可以为您完成所有注入工作:github.com/ninject/Ninject.Extensions.Factory/wiki/Lazy
    【解决方案3】:

    您还可以使用改进的属性来解决它,以避免不必要地访问设置器:

    public interface IMainService
    {
        void DoStuff();
    }
    
    public class MainClass
    {
        [Inject] public IOtherService OtherService { get; init; }
        public void DoStuff() { ... }
    }
    
    public class OtherService
    {
        public OtherService(IMainService main)
        {
            main.OtherService = this;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-10-01
      • 1970-01-01
      • 2010-09-12
      • 2021-10-02
      • 1970-01-01
      相关资源
      最近更新 更多