TL;DR
简短回答:添加INotifyWhenDisposed
给你的View
。处置视图。这将导致 ninject 自动处理所有绑定的东西InNamedScope
另外 ninject 也会取消引用这些对象。这将导致(最终)垃圾收集(除非您在其他地方坚持强引用)。
为什么你的实施不起作用
当视图被释放/被释放时,Ninject 不会得到通知。
这就是为什么 ninject 有一个计时器运行来检查范围对象是否仍然活着(活着=没有被垃圾收集)。如果作用域对象不再存在,它会处理/释放作用域中保存的所有对象。
我相信计时器默认设置为 30 秒。
现在这到底意味着什么?
- 如果没有内存压力,GC 可能需要很长时间才能对范围对象进行垃圾收集(或者他可能永远不会这样做)
- 一旦作用域对象被垃圾回收,作用域对象也可能需要大约 30 秒的时间来处理和释放
- 一旦 ninject 释放了范围对象,如果没有内存压力,GC 可能会花费很长时间来收集对象。
确定性地释放作用域对象
现在,如果您需要在释放范围时立即处置/释放对象,则需要添加INotifyWhenDisposed https://github.com/ninject/Ninject/blob/master/src/Ninject/Infrastructure/Disposal/INotifyWhenDisposed.cs范围对象(另见here https://groups.google.com/forum/#!topic/ninject/usm3b93ZYw4)。
对于命名范围,您需要将此接口添加到绑定的类型中DefinesNamedScope
- 在你的情况下View
.
根据 Ninject.Extensions.NamedScope 的集成测试,这就足够了:请参阅here https://github.com/ninject/Ninject.Extensions.NamedScope/blob/c8d8e4b99a1add1fc11f455034343606f1538b50/src/Ninject.Extensions.NamedScope.Test/NamedScopeIntegrationTest.cs#L84
注意:唯一真正具有确定性的是作用域对象的处置。
实际上,这通常也会显着缩短垃圾收集发生的时间。然而,如果没有内存压力,实际的收集仍然可能需要很长时间。
实现这个应该可以让单元测试通过。
注意:如果根对象被绑定InCallScope
那么这个解决方案不起作用(ninject 3.2.2 / NamedScope 3.2.0)。我认为这是由于一个错误InCallScope
但遗憾的是几年前我未能报告它(错误)。不过,我也可能错了。
证明实施INotifyWhenDisposed
在根对象中将处理子对象
public class View : INotifyWhenDisposed
{
public View(ViewModel viewModel)
{
ViewModel = viewModel;
}
public event EventHandler Disposed;
public ViewModel ViewModel { get; private set; }
public bool IsDisposed { get; private set; }
public void Dispose()
{
if (!this.IsDisposed)
{
this.IsDisposed = true;
var handler = this.Disposed;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
}
public class ViewModel : IDisposable
{
public bool IsDisposed { get; private set; }
public void Dispose()
{
this.IsDisposed = true;
}
}
public class IntegrationTest
{
private const string ScopeName = "ViewScope";
[Fact]
public void Foo()
{
var kernel = new StandardKernel();
kernel.Bind<View>().ToSelf()
.DefinesNamedScope(ScopeName);
kernel.Bind<ViewModel>().ToSelf()
.InNamedScope(ScopeName);
var view = kernel.Get<View>();
view.ViewModel.IsDisposed.Should().BeFalse();
view.Dispose();
view.ViewModel.IsDisposed.Should().BeTrue();
}
}
它甚至可以与DefineDependency
and WithCreatorAsConstructorArgument
我没有 dotMemory.Unit 但这会检查 ninject 是否保留对其缓存中对象的强引用:
namespace UnitTestProject
{
using FluentAssertions;
using Ninject;
using Ninject.Extensions.DependencyCreation;
using Ninject.Extensions.NamedScope;
using Ninject.Infrastructure.Disposal;
using System;
using Xunit;
public class UnitTest1
{
[Fact]
public void TestMethod()
{
// Arrange
var kernel = new StandardKernel();
const string namedScope = "namedScope";
kernel.Bind<View>().ToSelf()
.DefinesNamedScope(namedScope);
kernel.DefineDependency<View, Presenter>();
kernel.Bind<ViewModel>().ToSelf().InNamedScope(namedScope);
Presenter presenterInstance = null;
kernel.Bind<Presenter>().ToSelf()
.WithCreatorAsConstructorArgument("view")
.OnActivation(x => presenterInstance = x);
var view = kernel.Get<View>();
// named scope should result in presenter and view getting the same view model instance
presenterInstance.Should().NotBeNull();
view.ViewModel.Should().BeSameAs(presenterInstance.ViewModel);
// disposal of named scope root should clear all strong references which ninject maintains in this scope
view.Dispose();
kernel.Release(view.ViewModel).Should().BeFalse();
kernel.Release(view).Should().BeFalse();
kernel.Release(presenterInstance).Should().BeFalse();
kernel.Release(presenterInstance.View).Should().BeFalse();
}
}
public class View : INotifyWhenDisposed
{
public View()
{
}
public View(ViewModel viewModel)
{
ViewModel = viewModel;
}
public event EventHandler Disposed;
public ViewModel ViewModel { get; private set; }
public bool IsDisposed { get; private set; }
public void Dispose()
{
if (!this.IsDisposed)
{
this.IsDisposed = true;
var handler = this.Disposed;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
}
public class ViewModel
{
}
public class Presenter
{
public View View { get; set; }
public ViewModel ViewModel { get; set; }
public Presenter(View view, ViewModel viewModel)
{
View = view;
ViewModel = viewModel;
}
}
}