为什么Ninject不会使用InCallScope释放已处置的对象?

时间:2013-07-17 15:17:18

标签: c# .net-4.0 ninject ninject-extensions

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

我的绑定看起来像这样:

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对象。我想,它们是由我处置的,但我不确定是谁调用了处理方法。

为什么Ninject持有这些被处置的物品?我希望当我将主服务部署在using块的末尾时(由于InCallScope()而导致IRepository对象的范围),其范围内的每个对象都应由Ninject处理和释放。

编辑:在对这种模式不好的任何评论或回答之前,我知道它可能会更好。我知道我可以提取服务接口以实际使用DI并提高可测试性,我也知道我应该使用Func<IRepository>作为构造函数参数,并注入其中,并且每个服务都可以拥有它拥有处理存储库的可靠性。我现在没有时间进行这样的重构。

1 个答案:

答案 0 :(得分:4)

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

  • 没有人持有对service1的引用
  • service1本身是GC'd(因为你的线程睡眠时间为20秒,很有可能它被提升为第2代并且它们很少被释放)
  • 在service1为GC后执行缓存修剪,缓存修剪间隔默认为30秒。您可能希望尝试更短的间隔。
  • 除了上一点之外,您可以尝试通过在service1
  • 中实施Ninject.Infrastructure.Disposal.INotifyWhenDisposed来强制立即发布