依赖注入和IDisposable对象的生命周期

4
我正在尝试使用依赖注入方法(使用Ninject)开发一个库,但由于我的设计不正确,我感到有些困惑。简而言之,我的设计方法是:
  1. 一个父对象拥有一个公共对象。
  2. 一个父对象使用任意数量的子对象。
  3. 所有子对象都应该使用与其父对象相同的公共对象实例。
以下是我问题域的简单模型。
interface IParent : IDisposable {
    void Operation();
}
interface ICommon : IDisposable {
    void DoCommonThing();
}
interface IChild1 {
    void DoSomething();
}
interface IChild2 {
    void DoAnotherThing();
}
class Parent : IParent {
    private readonly ICommon _common;
    public Parent(ICommon common) {
        _common = common;
    }
    public void Dispose() {
        _common.Dispose();
    }
    public void Operation() {
        var c1 = ObjectFactory.GetInstance<IChild1>();
        c1.DoSomething();
        var c2 = ObjectFactory.GetInstance<IChild2>();
        c2.DoAnotherThing();
        // number of childs vary, do things until cn
        _common.DoCommonThing();
    }
}
class Common : ICommon {
    private bool _isDisposed;
    public void Dispose() {
        _isDisposed = true;
    }
    public void DoCommonThing() {
        if (_isDisposed) 
            throw new Exception("Common Object is Disposed");
    }
}
class Child1 : IChild1
{
    private readonly ICommon _common;
    public Child1(ICommon common) {
        _common = common;
    }
    public void DoSomething() {
        // Do Something...
        _common.DoCommonThing();
    }
}
class Child2 : IChild2 {
    private readonly ICommon _common;
    public Child2(ICommon common) {
        _common = common;
    }
    public void DoAnotherThing() {
        // Do Another Thing...
        _common.DoCommonThing();
    }
}

问题 1

child 对象的数量是不确定的。例如,根据 c1.DoSomething 的返回值,我可能需要其他的子对象,也可能不需要。因此,我不想通过构造函数注入它们,而是在需要时创建它们。但这种方法会违反 Hollywood 原则。

如何避免这种违规行为,而不是通过构造函数注入 child 对象?

问题 2

我希望 child 对象能够使用与其 parent 对象相同的 common 对象实例。因此,common 对象的生命周期应该与其父对象相同。

  1. 如果没有为 ICommon 定义生命周期,则所有 child 对象都将拥有自己的 common 对象实例。

  2. 如果 ICommon 的生命周期定义在 Thread 或 Request 作用域中,则我不能在同一线程或请求作用域中使用不同实例的 parent 对象。因为每个 parent 对象应该使用自己的全新的 common 对象并将其处理掉。

因此,我不能使用我所知道的生命周期范围选项来解决它。我对这个问题产生了另一个解决方案,但它会使代码变得更糟。

首先,不是将 ICommon 注入到 parent 对象中,而是通过 ObjectFactory 创建它自己的实例。

class Parent : IParent {
    private readonly ICommon _common;
    public Parent() {
        _common = ObjectFactory.GetInstance<ICommon>();
    }
.....

然后,父对象不再将ICommon注入到child对象中,而是设置子对象的common对象。
interface IChild {
    ICommon Common { get; set; }
}
interface IChildN : IChild {
     void DoNthThing();
}
abstract class ChildBase : IChild {
    ICommon IChild.Common { get; set; }
}
class ChildN : IChildN {
     public void DoNthThing() { }
}
class Parent : IParent {
    private readonly ICommon _common;
    public void Operation() {
        var c1 = ObjectFactory.GetInstance<IChild1>();
        c1.Common = _common;
        c1.DoSomething();
        var c2 = ObjectFactory.GetInstance<IChild2>();
        c2.Common = _common;
        c2.DoAnotherThing();
        _common.DoCommonThing();
    }
}

问题1

但是这个解决方案再次违反了好莱坞原则,我必须为每个child对象设置Common属性。

问题2

如何使用依赖注入(最好使用Ninject)让parent对象将其common对象分配给child对象?

问题3

这更一般地涉及到我的问题:如何正确地应用依赖注入到这个模型中?

注意:ObjectFactory.GetInstance 调用 Ninject 的Kernel.Get


如果有一个问题值得点赞,那么这个问题就是了! - Ruben Bartelink
寻找类似于InRequestScope的Ninject作用域。 - Ruben Bartelink
1个回答

3
你需要使用 CallScope 或者 NamedScope。它们是 Ninject.Extensions.NamedScope 包的一部分。这样可以将公共对象作用域限定到父级,以便所有子请求都接收相同的公共对象。
关于子对象的创建。如果你需要根据某个算法请求子对象,则需要使用工厂进行实例化。使用 Ninject.Extensions.Factory 包来实现这一点。这会进行上下文保留获取,并将父上下文传递给子请求,从而允许在由工厂创建的子对象中重用公共对象。
因此最终不需要使用自己的对象工厂。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接