-
Notifications
You must be signed in to change notification settings - Fork 10
Scopes
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.Register<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 Drizzts 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>();
}
Lifetime scopes are nestable, just think about the Singleton
lifetime whichs 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...
}
}
You have the option to attach a given scope to its parents lifetime, so when the parent is being disposed, the child will be disposed along with it.
using(var scope1 = container.BeginScope())
{
var scope2 = scope1.BeginScope(attachToParent: true);
//...
} // scope2 will be disposed as well
The lifetime scope tracks the resolved objects and disposes them (only if they are implements the IDisposable
interface) 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.Register<IDrow, Drizzt>(context => context.WithoutDisposalTracking());
During the registration of a service, you can set a custom finalizer delegate, which will be called when the scope used to instantiate your registered type is being disposed:
//The given delegate will be called when the scope is being disposed, before its actual disposal
container.Register<IDrow, Drizzt>(context => context.WithFinalizer(t => t.CallGuenhwyvarBack()));
When you are registering a service, you have the option to set its lifetime to a NamedScopeLifetime
by doing the following:
container.Register<IDrow, Drizzt>(context => context.InNamedScope("DefendOfMithralHall"));
container.Register<IDrow, Berginyon>(context => context.InNamedScope("AttackOfMithralHall"));
Then when you are opening a new scope, you can define by its name that which types you'd like to use:
using(var root = container.BeginScope("DefendOfMithralHall"))
{
var drow = root.Resolve<IDrow>(); // Drizzt will be resolved
using(var scope2 = root.BeginScope())
{
var drow2 = scope2.Resolve<IDrow>(); // Drizzt will be resolved because of the named parent scope
}
using(var scope3 = root.BeginScope("AttackOfMithralHall"))
{
var drow3 = scope3.Resolve<IDrow>(); // Berginyon will be resolved
}
}
You can configure a service to behave like a named scope, which means that upon its resolution a new dedicated named scope will be opened for managing its dependencies.
container.Register<IEvent, AttackOfMithralHall>(context => context.DefinesScope("AttackMithralHall"));
container.Register<IEvent, DefendOfMithralHall>(context => context.DefinesScope("DefendOfMithralHall"));
The lifetime of the internal named scope will be attached to that ones lifetime, which was used to create the service.
You have the option to bind an already instantiated service to a lifetime scope. which means that the service's lifetime will be tracked by the scope and will be disposed when the scope is being disposed.
using(var scope = container.BeginScope())
{
scope.PutInstanceInScope<IDrow>(drizzt);
}
You can disable the lifetime tracking by passing true
for the withoutDisposalTracking
parameter, then only the strong reference to the instance will be dropped when the scope is being disposed.
using(var scope = container.BeginScope())
{
scope.PutInstanceInScope<IDrow>(drizzt, true);
}
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.Register<IDrow, Drizzt>();
IDrow drizzt;
IBarbarian wulfgar;
//Create a child scope
using(var child = container.CreateChildContainer())
{
//Register to the child container
child.Register<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
}
Child scopes are also nestable:
using(var child1 = container.CreateChildContainer())
{
using(var child2 = child1.CreateChildContainer())
{
//etc...
}
}
- Service registration
- Factory registration
- Assembly registration
- Composition
- Fluent registration api
- Service resolution
- Resolution by attributes
- Conventional resolution
- Delegate resolution
- Conditional resolution
- Multi resolution
- Lifetimes
- Generics
- Generic wrappers
- Decorators
- Resolvers
- Scopes
- Container configuration
- Container diagnostics
- Exceptions