Lifestyles
When you have two classes: HomeViewModel
and ApplicationSettingsViewModel
, both with dependency on IUserService
, should they both have the same instance of the user service, or each get their own?
When you go to application settings window, close it, and then open again, should you get the same instance of ApplicationSettingsViewModel
or a new one?
When a component's instance is no longer needed, who and how should clean up after it: dispose it if needed, unsubscribe from receiving event aggregator's events and so on?
The answer is - it depends. The behavior is controlled by what's called the lifestyle of your components. Lifestyle controls in what scope instances are reused, and when to release them (that is do all the necessary clean up steps and then let it go for the Garbage Collector to destroy).
Standard lifestyles: the common ones
Out of the box Windsor provides set of lifestyles that cover most real life scenarios.
Singleton
Singleton components will only produce a single instance that is bound to the container. The instance will be created the first time someone requests it, and subsequently reused every time it's needed. Explicitly releasing singletons (by calling container.Release(mySingleton)
) does nothing. The sole instance will be released when the container it's registered with is disposed.
This is how you register a component as singleton:
Container.Register(Component.For<MySingletonComponent>().LifestyleSingleton());
Container.Register(Component.For<MySingletonComponent>());
Transient
Transient can be seen as the opposite to singleton. Transient components are not bound to any tangible scope. Each time an instance of a transient component is needed, container will produce a new one, never reusing them. You can say that the scope of a transient's instance is dictated by its user. Therefore transient instances are released when the object using them is released.
If your transient component is the root of the graph (that is the class that uses it is not managed by the container) resolved explicitly via container.Resolve<MyTransientComponent>()
you explicitly end its lifetime scope by calling container.Release(myTransientInstance)
when you no longer need it.
This is how you register a component as transient:
Container.Register(Component.For<MyTransientComponent>().LifestyleTransient());
Release
what you Resolve
d: Some people, especially those who used certain other containers in the past, sometimes forget that Windsor may track transient components. They Resolve
the instances, and never Release
them. To ensure proper components lifecycle management Windsor may track those components. That means that unless you release them, Garbage Collector will not be able to reclaim them, and you'll end up with de-facto memory leak. So remember this useful rule of thumb: Remember to Release
what you explicitly Resolve
d.
Standard lifestyles: the less common ones
The lifestyles described above are the bread and butter of most applications. Occasionally though you'll need a more specialized lifestyle.
Scoped
Windsor 3 added option to specify arbitrary scope of component instance lifetime/reuse. Here's how you do it:
Container.Register(Component.For<MyScopedComponent>().LifestyleScoped());
Now consider the following scenario:
using Castle.MicroKernel.Lifestyle;
using (Container.BeginScope()) //extension method
{
var one = Container.Resolve<MyScopedComponent>();
var two = Container.Resolve<MyScopedComponent>();
Assert.AreSame(one, two);
} // releases the instance
In the code above the using block encloses the scope of reuse (whenever an instance is needed inside the scope the same will be used) and lifetime (end of the using block releases the instance.
CallContext
scope: For the more inquisitive of you, the scope is bound to the CallContext. What that mean is that it is available on the current thread, but also flows to thread pool and Task
threads. In multi threaded scenarios however, be careful to ensure the child operation finishes before the end of using block on the parent thread is executed.
Custom scopes
In addition to the default, CallContext
scope, you can bind your components to any arbitrary scope you choose, by implementing Castle.MicroKernel.Lifestyle.Scoped.IScopeAccessor
interface. (see implementing custom scope).
You then specify the scope accessor when registering your component:
Container.Register(Component.For<MyScopedComponent>().LifestyleScoped<MyCustomScopeAccessor>());
Bound
Look at the following diagram.
Somewhere in the graph we have two view models, one depending on the other, and both of them depend on some other service, say a repository. You might want to bind the repository to the subgraph. In other words you might want the entire subgraph of the outermost view model (WelcomeScreenViewModel
) to share the same instance of the repository, and to have the repository released when the view model itself is released.
This is what bound lifestyle is for.
Container.Register(Component.For<Repository>().LifestyleBoundTo<ViewModelBase>());
ViewModelBase
common base type of all view models. The binding doesn't look at services exposed by the container, but the actual implementation type of the component, and checks if it is assignable to the specified type.
Bound to nearest
In some cases instead of binding to the farthest matched object in the graph you may want to bind to the closest. That is WelcomeScreenViewModel
will share the instance of the Repository
with its (direct and indirect) dependencies, but once one of those dependencies happens to be another view model, it will get a new Repository
for itself and its dependencies (direct and indirect) unless one of those dependencies happens to be yet another view model... You get the idea.
In that case you register the Repository
using BoundToNearest
Container.Register(Component.For<Repository>().LifestyleBoundToNearest<ViewModelBase>());
Bound: custom
When the default options are not enough you can provide your custom way to select the component to bind to by using the following overload of BoundTo
method:
BoundTo(Func<IHandler[], IHandler> scopeRootBinder)
You can pass a custom delegate that from the collection of the IHandler
s representing components in the subgraph (from the outermost to innermost) selects the one you want to bind your component to.
Standard lifestyles: the very rarely used ones
The lifestyles described above are all that 99% of applications will ever need. Feel free to stop reading here, as chances are you will never need or even see any of the lifestyles described below. For some corner cases, Windsor provides additional lifestyles.
PerThread
Instance of a component will be shared in scope of a single thread of execution. It will be created the first time the component is requested on given thread. Releasing the component explicitly does nothing. Instances will be released when the container they're registered with is disposed.
Task
s) are involved. When in doubt - avoid.
Pooled
A pool of instances will be created and then one of them will be returned when requested. Poolable lifestyle has two parameters that influence its behavior - initialSize
and maxSize
.
When the component is first requested, the pool of initialSize
elements is instantiated and a single one of them is marked internally as in use and returned. When more components are requested, the pool will first return all of the components it has that are not in use, and if it runs out, will start creating new ones. Releasing the components may do either of two things:
- When the pool has more components in use than
maxSize
the component will be released immediately - Otherwise the component will be recycled (if it implements
IRecyclable
) and returned to the pool marked as ready to use.
IRecyclable
interface
The Windsor provides a special interface - Castle.Core.IRecyclable
for poolable components. It contains single method:
void Recycle();
This method is invoked when the component is returned to the pool, and components can use it to implement custom initialization/clean up logic.
Custom lifestyles
Allows you to set your own implementation of ILifestyleManager
for the component. Also used be some facilities, like WCF Facility which provides two additional lifestyles - per WCF session and per WCF operation.
Setting lifestyle
The most common way to specify a lifestyle is through the registration API. You can do it for a single component, as shown in the examples above. The same methods are also available when registering a set of types. For example here's how you can make all your controllers transient when registering:
Container.Register(
Classes.FromThisAssembly()
.BasedOn<IController>()
.LifestyleTransient());
Via XML
It is also possible to set lifestyle from XML configuration.
Via Attributes
Windsor provides set of attributes that you can use to set suggested lifestyle for your components.
The attributes are available in Castle.Core
namespace, and all of them inherit from Castle.Core.LifestyleAttribute
.
There's an attribute for each lifestyle described above, each of them named after the lifestyle.
Here's how you mark a type as transient using TransientAttribute
attribute.
[Transient]
public class MyTransientComponent
{
// something here...
}