TL DR
short answer: add INotifyWhenDisposed to your View . Dispose of the view. This will cause ninject to automatically delete all related InNamedScope plus things, and also ninject will not reference these objects. This will result in (final) garbage collection (unless you approach strong links elsewhere).
Why your implementation does not work
Ninject does not receive information when a view is released / deleted. Therefore, ninject has a timer that checks if the scope object is alive (live = not garbage collected). If the scope object is no longer alive, it allocates / frees all objects that have been stored in scope.
I assume the default timer is set to 30 seconds.
Now what does that mean?
- If there is no memory pressure, the GC may take a long time until the area object is garbage collected (or it may not, someday).
- After the object-object is collected using garbage, it may take about 30 seconds to place objects with areas and release them.
- After ninject has released scope objects, again the GC can take a long time to collect the object if there is no memory pressure.
Deterministic selection of objects with an area
Now, if you need the objects to be deleted or released immediately after releasing the area, you need to add INotifyWhenDisposed to (also see here ). Using these areas, you will need to add this interface to the type that is associated with DefinesNamedScope - in your case, View .
According to the integration tests Ninject.Extensions.NamedScope this will be enough: see here
Note. The only thing that is really determined is the removal of objects with scope. In practice, this usually reduces garbage collection time. However, if there is no memory pressure, again, the actual collection can take a long time.
Implementing this should pass the unit test.
Note: if the root object is bound by InCallScope , then this solution does not work (ninject 3.2.2 / NamedScope 3.2.0). I think this is due to an error with InCallScope , but, unfortunately, I was not able to report this (error) several years ago. I could be wrong too.
Evidence that the INotifyWhenDisposed implementation on the root will have children
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(); } }
It even works with DefineDependency and WithCreatorAsConstructorArgument
I don't have dotMemory.Unit, but this checks if ninject keeps a strong reference to objects in the cache:
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() {