Singletons
This one is easy, any object marked as the Singleton lifecycle that implements IDisposable
is disposed when the root container is
disposed:
[Fact]
public void singletons_are_disposed_when_the_container_is_disposed()
{
var container = new Container(_ =>
{
_.ForSingletonOf<DisposableSingleton>();
});
// As a singleton-scoped object, every request for DisposableSingleton
// will return the same object
var singleton = container.GetInstance<DisposableSingleton>();
singleton.ShouldBeSameAs(container.GetInstance<DisposableSingleton>());
singleton.ShouldBeSameAs(container.GetInstance<DisposableSingleton>());
singleton.ShouldBeSameAs(container.GetInstance<DisposableSingleton>());
singleton.ShouldBeSameAs(container.GetInstance<DisposableSingleton>());
singleton.WasDisposed.ShouldBeFalse();
// now, dispose the Container
container.Dispose();
// the SingletonThing scoped object should be disposed
singleton.WasDisposed.ShouldBeTrue();
}
Ejecting a singleton-scoped object will also force it to be disposed. See Using the Container Model for more information on how to eject singletons
from a running Container
.
Nested Containers
As discussed in Nested Containers (Per Request/Transaction), any transient, AlwaysUnique (as of 4.0), or container-scoped object that implements IDisposable
and is created
by a nested container will be disposed as the nested container is disposed:
[Fact]
public void nested_container_disposal()
{
var container = new Container(_ =>
{
// A SingletonThing scoped service
_.ForSingletonOf<IColorCache>().Use<ColorCache>();
// A transient scoped service
_.For<IColor>().Use<Green>();
// An AlwaysUnique scoped service
_.For<Purple>().AlwaysUnique();
});
ColorCache singleton = null;
Green nestedGreen = null;
Blue nestedBlue = null;
Purple nestedPurple = null;
using (var nested = container.GetNestedContainer())
{
// SingletonThing's are really built by the parent
singleton = nested.GetInstance<IColorCache>()
.ShouldBeOfType<ColorCache>();
nestedGreen = nested.GetInstance<IColor>()
.ShouldBeOfType<Green>();
nestedBlue = nested.GetInstance<Blue>();
nestedPurple = nested.GetInstance<Purple>();
}
// Transients created by the Nested Container
// are disposed
nestedGreen.WasDisposed.ShouldBeTrue();
nestedBlue.WasDisposed.ShouldBeTrue();
// Unique's created by the Nested Container
// are disposed
nestedPurple.WasDisposed.ShouldBeTrue();
// NOT disposed because it's owned by
// the parent container
singleton.WasDisposed.ShouldBeFalse();
}
Transients
Regardless of the new tracking/release mode, the StructureMap team still strongly recommends using a nested container per HTTP request or service bus message handling session or logical transaction to deal with disposing transient objects.
By default, StructureMap will not hang on to any transient-scoped objects created by the root or child containers. Dealing with
IDisposable
is completely up to the user in this case. The StructureMap team has long believed that trying to track transient disposables with
an explicit Release(object)
mode as other IoC containers behave would do more harm than good (memory leaks from forgetting to call Release(), more work on the user).
That all being said, in order to comply with the new ASP.Net vNext compliance behavior, StructureMap 4.0 introduces a new opt-in transient tracking mode with the prerequisite Release(object)
method:
[Fact]
public void release_transient_created_by_root_container()
{
var container = new Container(_ => _.TransientTracking = TransientTracking.ExplicitReleaseMode);
container.TransientTracking.ShouldBeOfType<TrackingTransientCache>();
var transient1 = container.GetInstance<DisposableGuy>();
var transient2 = container.GetInstance<DisposableGuy>();
transient1.WasDisposed.ShouldBeFalse();
transient2.WasDisposed.ShouldBeFalse();
// release ONLY transient2
container.Release(transient2);
transient1.WasDisposed.ShouldBeFalse();
transient2.WasDisposed.ShouldBeTrue();
// transient2 should no longer be
// tracked by the container
container.TransientTracking.Tracked
.Single()
.ShouldBeTheSameAs(transient1);
}
[Fact]
public void disposing_the_container_disposes_tracked_transients()
{
var container = new Container(_ => _.TransientTracking = TransientTracking.ExplicitReleaseMode);
var transient1 = container.GetInstance<DisposableGuy>();
var transient2 = container.GetInstance<DisposableGuy>();
transient1.WasDisposed.ShouldBeFalse();
transient2.WasDisposed.ShouldBeFalse();
container.Dispose();
transient1.WasDisposed.ShouldBeTrue();
transient2.WasDisposed.ShouldBeTrue();
}
As of right now, StructureMap will only track the top level object requested from a Container
and not all the other IDisposable
objects that may
have been created as part of the main object graph.