3

我正在尝试在 WinForms 应用程序中使用 Ninject(版本 3.0.1),我有几个(当前)自绑定服务类,我使用 Ninject 构建它们。一些服务类需要其他服务类(子服务)。这些服务类中的大多数都需要一个存储库来与数据库交互,因为我有一个抽象的 IRepository 接口。我需要为服务类中的整个服务层次结构拥有相同的存储库,因此我InCallScope()在绑定 IRepository 时使用范围。目前我使用 XPO 作为 ORM 工具,所以我有一个 XpoRepository 实现,我绑定到它。请参阅我关于此场景的其他问题。

我的绑定看起来像这样:

Bind<IRepository>().To<XpoRepository>().InCallScope();

我没有ToSelf()为每个服务类提供显式绑定,因此我假设当我从 Ninject 获取它们时,它们应该具有瞬态范围,我将其解释为我必须手动处置它们。

假设我有一个 Services1 和一个 Services2 服务类,它们都有一个 IRepository 类型的构造函数参数。现在假设 Services1 想使用 Services2 的一些方法,所以我向 Services1 添加了另一个构造函数参数,类型为 Services2。如果没有 Ninject,我会这样做:

var repo = new MyRepository(); // implementing IRepository
var service1 = new Services1(repo, new Services2(repo));

我在后台线程中使用其中一项服务(使用 TPL),在这样的循环中:

while (true) {
    if (CancellatioPending())
        break;
    using (var service = _kernel.Get<Service1>())
    {
        // do some stuff using the service class
    }
    Thread.Sleep(20*1000);
}

在使用 Ninject 之前我有相同的结构,所以我(我认为)正确地实现了对每个对象的处理,包括正确位置的存储库。但是,我注意到,由于我为此使用了 Ninject,因此我的应用程序中存在很大的内存泄漏,并且它每 2-3 小时就会因 OutOfMemoryException 而崩溃。我在循环中放置了一个断点,并注意到 Ninject 缓存有数千个条目,其中充满了已处置的 XpoRepository 对象。我猜它们是由我处理的,但我不确定是谁调用了 dispose 方法。

为什么 Ninject 持有这些已处置的对象?我希望当我在 using 块的末尾处置主要服务时(这是 IRepository 对象的范围,由于 InCallScope()),其范围内的每个对象都应该由 Ninject 处置和释放。

编辑:在任何评论或回答为什么这种模式不好之前,我知道它可能会更好。我知道我可以提取服务接口以实际使用 DI 并提高可测试性,我也知道我可能应该使用 aFunc<IRepository>作为构造函数参数并注入其中,就像每个服务都可以有自己的责任来处理存储库。只是我目前没有时间进行此类重构。

4

1 回答 1

4

如果满足以下所有条件,Ninject 将释放存储库:

  • 没有人持有对 service1 的引用
  • service1 本身是 GC 的(因为你有 20 秒的线程睡眠,它很有可能已被提升到第 2 代并且它们很少被释放)
  • 缓存修剪是在 service1 被 GC 后执行的,缓存修剪间隔默认为 30 秒。您可能想尝试更短的间隔。
  • 或者,您可以尝试通过Ninject.Infrastructure.Disposal.INotifyWhenDisposed在 service1 中实施来强制立即释放
于 2013-07-18T11:11:37.907 回答