Fork me on GitHub

Supported Lifecycles Edit on GitHub


Out of the box, the core StructureMap assembly supports these lifecycles:

  • Transient -- The default lifecycle. A new object is created for each logical request to resolve an object graph from the container.
  • Singleton -- Only one object instance will be created for the container and any children or nested containers created by that container
  • ContainerScoped -- Only one object instance will be created for unique root, child, or nested container
  • AlwaysUnique -- A new object instance is created every time, even within the same object graph
  • ThreadLocal -- Only one object instance will be created for the currently executing Thread

Transient

Older versions of StructureMap referred to Transient as PerRequest, which might be a more accurate reflection of how this lifecycle behaves but causes some confusion with ASP.NET HTTP scoping. The easiest way to think of Transient is that a single object instance will be created for each top level call to Container.GetInstance() (or any other object resolution method on the IContainer interface). Transient objects resolved from a nested container, Transients are scoped to the lifecycle of the nested container itself. See Nested Containers (Per Request/Transaction) for more information.

StructureMap's behavior for transient objects that implement IDisposable changed in 4.0 to introduce an "opt-in" tracking mode. Please see StructureMap and IDisposable for the details.

The following unit test demonstrates how Transient lifecycles work in both root and nested containers.


[Fact]
public void Transient()
{
    var c = new Container(x => { x.For<IService>().Use<Service>().Transient(); });

    // In a normal container, you get a new object
    // instance of the Service class in subsequent
    // requests
    c.GetInstance<IService>()
        .ShouldNotBeTheSameAs(c.GetInstance<IService>())
        .ShouldNotBeTheSameAs(c.GetInstance<IService>());

    // Within a nested container, 'Transient' now
    // means within the Nested Container.
    // A nested container is effectively one request
    using (var nested = c.GetNestedContainer())
    {
        nested.GetInstance<IService>()
            .ShouldBeTheSameAs(nested.GetInstance<IService>())
            .ShouldBeTheSameAs(nested.GetInstance<IService>());
    }
}

Also note that a transient dependency will be created exactly once in an object graph resolved from IContainer.GetInstance(Type). Imagine that you are building an object graph with various objects that all need to apply some work to a shared unit of work object (think NHibernate's ISession, Entity Framework's DbContext, RavenDb's IDocumentSession):


public interface IUnitOfWork
{
}

public class DefaultUnitOfWork : IUnitOfWork
{
}

public class Worker1
{
    public IUnitOfWork Uow { get; set; }

    public Worker1(IUnitOfWork uow)
    {
        Uow = uow;
    }
}

public class Worker2
{
    public IUnitOfWork Uow { get; set; }

    public Worker2(IUnitOfWork uow)
    {
        Uow = uow;
    }
}

public class WorkerCoordinator
{
    public IUnitOfWork Uow { get; set; }
    public Worker1 Worker1 { get; set; }
    public Worker2 Worker2 { get; set; }

    public WorkerCoordinator(IUnitOfWork uow, Worker1 worker1, Worker2 worker2)
    {
        Uow = uow;
        Worker1 = worker1;
        Worker2 = worker2;
    }
}

[Fact]
public void transient_scoped_Instance_is_built_once_per_resolution_to_the_Container()
{
    var container = new Container(_ => { _.For<IUnitOfWork>().Use<DefaultUnitOfWork>(); });

    var coordinator = container.GetInstance<WorkerCoordinator>();

    // The IUnitOfWork object instance is the same for
    // all the objects in the object graph that had
    // a constructor dependency on IUnitOfWork
    coordinator.Uow
        .ShouldBeTheSameAs(coordinator.Worker1.Uow);

    coordinator.Uow
        .ShouldBeTheSameAs(coordinator.Worker2.Uow);

    coordinator.Worker1.Uow
        .ShouldBeTheSameAs(coordinator.Worker2.Uow);
}

AlwaysUnique

Very simply, using the AlwaysUnique means that a new object instance will be created every single time a configured Instance is either requested from a Container or as a dependency to another object. The AlwaysUnique lifecycle is a "fire and forget" operation as the object instances are neither tracked nor disposed by StructureMap.


[Fact]
public void Always_Unique()
{
    var c = new Container(x => { x.For<IService>().Use<Service>().AlwaysUnique(); });

    // In a normal container, you get a new object
    // instance of the Service class in subsequent
    // requests
    c.GetInstance<IService>()
        .ShouldNotBeTheSameAs(c.GetInstance<IService>())
        .ShouldNotBeTheSameAs(c.GetInstance<IService>());

    // Within a nested container, 'Transient' now
    // means within the Nested Container.
    // A nested container is effectively one request
    using (var nested = c.GetNestedContainer())
    {
        nested.GetInstance<IService>()
            .ShouldNotBeTheSameAs(nested.GetInstance<IService>())
            .ShouldNotBeTheSameAs(nested.GetInstance<IService>());
    }

    // Even in a single request,
    var holder = c.GetInstance<ServiceUserHolder>();
    holder.Service.ShouldNotBeTheSameAs(holder.User.Service);
}

Singleton

StructureMap 3.0 fixed the dreaded singletons with nested container's bug that was so problematic in 2.6.


[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();
}

Do note that objects created as the singleton scope will be disposed when the Container is disposed if they implement the IDisposable interface:


public interface IUnitOfWork
{
}

public class DefaultUnitOfWork : IUnitOfWork
{
}

public class Worker1
{
    public IUnitOfWork Uow { get; set; }

    public Worker1(IUnitOfWork uow)
    {
        Uow = uow;
    }
}

public class Worker2
{
    public IUnitOfWork Uow { get; set; }

    public Worker2(IUnitOfWork uow)
    {
        Uow = uow;
    }
}

public class WorkerCoordinator
{
    public IUnitOfWork Uow { get; set; }
    public Worker1 Worker1 { get; set; }
    public Worker2 Worker2 { get; set; }

    public WorkerCoordinator(IUnitOfWork uow, Worker1 worker1, Worker2 worker2)
    {
        Uow = uow;
        Worker1 = worker1;
        Worker2 = worker2;
    }
}

[Fact]
public void transient_scoped_Instance_is_built_once_per_resolution_to_the_Container()
{
    var container = new Container(_ => { _.For<IUnitOfWork>().Use<DefaultUnitOfWork>(); });

    var coordinator = container.GetInstance<WorkerCoordinator>();

    // The IUnitOfWork object instance is the same for
    // all the objects in the object graph that had
    // a constructor dependency on IUnitOfWork
    coordinator.Uow
        .ShouldBeTheSameAs(coordinator.Worker1.Uow);

    coordinator.Uow
        .ShouldBeTheSameAs(coordinator.Worker2.Uow);

    coordinator.Worker1.Uow
        .ShouldBeTheSameAs(coordinator.Worker2.Uow);
}

ContainerScoped

New in StructureMap 4.0 is the ContainerScoped lifecycle that was designed specifically for compliance with the new ASP.Net DNX adapter model. ContainerScoped in this case means that a registration will be built once per Container, such that the root container, any child or profile container, and every single nested container will build its own object instance.

The acceptance test for ContainerScoped is shown below:


[Fact]
public void container_scoping_with_root_child_and_nested_container()
{
    var container = new Container(_ =>
    {
        _.ForConcreteType<Disposable>().Configure.ContainerScoped();
    });

    var child = container.CreateChildContainer();

    var nested = container.GetNestedContainer();

    // Always the same object when requested from the root container
    var mainDisposable = container.GetInstance<Disposable>();
    mainDisposable
        .ShouldBeTheSameAs(container.GetInstance<Disposable>());

    // Always the same object when requested from a child container
    var childDisposable = child.GetInstance<Disposable>();
    childDisposable
        .ShouldBeTheSameAs(child.GetInstance<Disposable>());

    // Always the same object when requested from a nested container
    var nestedDisposable = nested.GetInstance<Disposable>();
    nestedDisposable
        .ShouldBeTheSameAs(nested.GetInstance<Disposable>());

    // It should be a different object instance for
    // all three containers
    mainDisposable
        .ShouldNotBeTheSameAs(childDisposable);

    mainDisposable
        .ShouldNotBeTheSameAs(nestedDisposable);

    childDisposable
        .ShouldNotBeTheSameAs(nestedDisposable);

    // When the nested container is disposed,
    // it should dispose all the container scoped objects,
    // but not impact the other containers
    nested.Dispose();
    nestedDisposable.WasDisposed.ShouldBeTrue();
    childDisposable.WasDisposed.ShouldBeFalse();
    mainDisposable.WasDisposed.ShouldBeFalse();

    // Same for the child container
    child.Dispose();
    childDisposable.WasDisposed.ShouldBeTrue();
    mainDisposable.WasDisposed.ShouldBeFalse();

    // Same for the main container
    container.Dispose();
    mainDisposable.WasDisposed.ShouldBeTrue();
}

ThreadLocal

The ThreadLocalStorage based lifecycle is seldom used, but the easiest example of using it and explanation is the integration test:



public class ThreadLocalStorageLifecycleTester
{
    public ThreadLocalStorageLifecycleTester()
    {
        _lifecycle = new ThreadLocalStorageLifecycle();

        container = new Container(x => x.For<Rule>(Lifecycles.ThreadLocal).Use(() => new ColorRule("Red")));
    }

    private ThreadLocalStorageLifecycle _lifecycle;
    private ColorRule _rule1;
    private ColorRule _rule2;
    private ColorRule _rule3;
    private Container container;

    private void findRule1()
    {
        _rule1 = container.GetInstance<Rule>().ShouldBeOfType<ColorRule>();

        var rule = container.GetInstance<Rule>().ShouldBeOfType<ColorRule>();
        _rule1.ShouldBeTheSameAs(rule);
    }

    private void findRule2()
    {
        _rule2 = container.GetInstance<Rule>().ShouldBeOfType<ColorRule>();

        var rule = container.GetInstance<Rule>().ShouldBeOfType<ColorRule>();
        _rule2.ShouldBeTheSameAs(rule);
    }

    private void findRule3()
    {
        _rule3 = container.GetInstance<Rule>().ShouldBeOfType<ColorRule>();

        var rule = container.GetInstance<Rule>().ShouldBeOfType<ColorRule>();
        _rule3.ShouldBeTheSameAs(rule);

        rule = container.GetInstance<Rule>().ShouldBeOfType<ColorRule>();
        _rule3.ShouldBeTheSameAs(rule);

        rule = container.GetInstance<Rule>().ShouldBeOfType<ColorRule>();
        _rule3.ShouldBeTheSameAs(rule);

        rule = container.GetInstance<Rule>().ShouldBeOfType<ColorRule>();
        _rule3.ShouldBeTheSameAs(rule);
    }

    [Fact]
    public void object_has_been_created()
    {
        container.Model.For<Rule>().Default.ObjectHasBeenCreated().ShouldBeFalse();
        var r1 = container.GetInstance<Rule>();
        container.Model.For<Rule>().Default.ObjectHasBeenCreated().ShouldBeTrue();
    }

    [Fact]
    public void FindUniqueInstancePerThread()
    {
        var t1 = new Thread(findRule1);
        var t2 = new Thread(findRule2);
        var t3 = new Thread(findRule3);

        t1.Start();
        t2.Start();
        t3.Start();

        t1.Join();
        t2.Join();
        t3.Join();

        _rule1.ShouldNotBeTheSameAs(_rule2);
        _rule1.ShouldNotBeTheSameAs(_rule3);
        _rule2.ShouldNotBeTheSameAs(_rule3);
        (_rule1.ID != _rule2.ID).ShouldBeTrue();
        (_rule1.ID != _rule3.ID).ShouldBeTrue();
        (_rule2.ID != _rule3.ID).ShouldBeTrue();
    }
}