Skip to content
Peter Csajtai edited this page May 16, 2017 · 19 revisions

Lifetime scope

A lifetime scope is a unit within your scoped services are being reused and shared by their consumers. It can be interpreted as the unit-of-work pattern where the lifetime scope encapsulates a given unit and the services required for the work are being resolved from it, and when the work finishes the services are being disposed with it.

class Drizzt : IDrow
{
    [Dependency("Icingdeath")]
    public IWeapon RightHand { get; set; }
	
    [Dependency("Twinkle")]
    public IWeapon LeftHand { get; set; }
}

//Register Twinkle as a scoped service
container.RegisterScoped<IWeapon, Twinkle>("Twinkle");

//Register Icingdeath as a singleton service
container.RegisterSingleton<IWeapon, Icingdeath>("Icingdeath");

//Register Drizzt as a transient service
container.RegisterType<IDrow, Drizzt>();

//This will create a scoped instance within the root scope, so it can be interpreted as a singleton
var twinkle = container.Resolve<IWeapon>("Twinkle");

using(var scope = container.BeginScope())
{
    //A new 'Twinkle' instance will be injected into Drizzt's left hand,
    //cause it was registered as a scoped service and was resolved from a lifetime scope.
    //'Icingdeath' will be resolved from the root scope, 
    //cause it was registered as a singleton.
    var drizzt = scope.Resolve<IDrow>();
}

Nested lifetime scopes

Lifetime scopes are nestable, just think about the Singleton lifetime which's service is always resolved from the root scope, so it's shared between all scopes.

using(var scope1 = container.BeginScope())
{
    using(var scope2 = scope1.BeginScope())
    {
        //etc...
    }
}

Disposal

The lifetime scope tracks the resolved disposable objects and disposes them when the scope is being disposed. You can control this behavior with some configuration:

//This will include transient objects into the disposal tracking
var container = new StashboxContainer(config => config.WithDisposableTransientTracking());

//This will exclude the given registration from the disposal tracking
container.RegisterType<IDrow, Drizzt>(context => context.WithoutDisposalTracking());

Cleanup delegate

You can also specify a custom action delegate which will be invoked when a scope closes:

//The given delegate will be called when the scope is being disposed, before the registered service's disposal
container.RegisterType<IDrow, Drizzt>(context => context.WithFinalizer(t => t.CallGuenhwyvarBack()));

Injecting into a lifetime scope

You also have the ability to put additional services into a given lifetime scope by calling the PutInstanceInScope() method:

using(var scope = container.BeginScope())
{
    scope.PutInstanceInScope<IDrow>(drizzt);
}

Services injected in this way will also being disposed by the scope, except if you set the withoutDisposalTracking parameter to true:

using(var scope = container.BeginScope())
{
    scope.PutInstanceInScope<IDrow>(drizzt, true);
}

Child scopes

With a child scope you can build up a parent-child relationship between containers. This means you can have a different subset of services present in child and parent containers, if something is missing from a child the parent will be asked to resolve the service:

var container = new StashboxContainer();

//Create a child scope
var child = container.CreateChildContainer();

The child container maintains the lifetime of those services only which were registered into it:

using(var container = new StashboxContainer(config => config.WithDisposableTransientTracking()))
{
    //Register to the parent container
    container.RegisterType<IDrow, Drizzt>();
    
    IDrow drizzt;
    IBarbarian wulfgar;
    
    //Create a child scope
    using(var child = container.CreateChildContainer())
    {
        //Register to the child container
        child.RegisterType<IBarbarian, Wulfgar>();
        
        drizzt = child.Resolve<IDrow>();
        wulfgar = child.Resolve<IBarbarian>();
    } //At the end of the scope only Wulfgar will be disposed because only him was maintained by the child container
}

Nested child scopes

Child scopes are also nestable:

using(var child1 = container.CreateChildContainer())
{
    using(var child2 = child1.CreateChildContainer())
    {
        //etc...
    }
}