The case
{
}
public class MySingleton: IMySingleton
{
public void Dispose()
{
}
}
public interface IMyTransient
{
}
public class MyTransient : IMyTransient
{
private readonly IMySingleton singleton;
public MyTransient(IMySingleton singleton)
{
this.singleton = singleton;
}
}
The transient is from the point of view of its usage, that mean that its lifecycle is managed by an “external context” and, as is, it does not need a special “destructor”. The “external context” may hold the instance somewhere or may use it only in the context of a method and can quite leave the destruction to the garbage collector. If/when MyTransient implements IDisposable the “external context” should dispose the instance. You can see this behavior in various places for example in the DefaultControllerFactory of Asp.NET MVC or in the IInstanceProvider of WCF (both has a ReleaseInstance cheking for IDisposable); personally I have this situation in other places.
If I do something like this
The test using Windsor with Transient Lifestyle
{
private WindsorContainer GetContainerConfiguredWindsorContainer()
{
var container = new WindsorContainer();
container.Register(Component.For<IMySingleton>().ImplementedBy<MySingleton>());
container.Register(Component.For<IMyTransient>().ImplementedBy<MyTransient>().LifeStyle.Transient);
return container;
}
[Test]
public void WhenTransientRequiredThenReturnDifferentInstances()
{
using (WindsorContainer container = GetContainerConfiguredWindsorContainer())
{
var t0 = container.Resolve<IMyTransient>();
var t1 = container.Resolve<IMyTransient>();
t0.Should().Not.Be.SameInstanceAs(t1);
}
}
[Test]
public void WhenTransientRequiredThenContainerShouldntHaveInstancesOfMyTransient()
{
using (WindsorContainer container = GetContainerConfiguredWindsorContainer())
{
var t0 = container.Resolve<IMyTransient>();
var t1 = container.Resolve<IMyTransient>();
container.Kernel.ReleasePolicy.Satisfy(rp=> !rp.HasTrack(t0));
container.Kernel.ReleasePolicy.Satisfy(rp => !rp.HasTrack(t1));
}
}
}
Now take care : The second test fails. That means that the container is holding two instances of MyTransient class; let me show you where:
Perhaps there is a good explication for this behavior but I must admit that I can’t understand why all instances of MyTransient should have its lifecycle stuck to the lifecycle of MySingleton only because MySingleton is disposable… bah?!? by the way that is not a matter because Castle.Windsor give us the ability to define the behavior we need.
The solution
First of all I need my custom lifestyle manager:public class InstantiateAndForgetIt : ILifestyleManager
{
private IComponentActivator componentActivator;
public void Init(IComponentActivator componentActivator, IKernel kernel, ComponentModel model)
{
this.componentActivator = componentActivator;
}
public object Resolve(CreationContext context)
{
return componentActivator.Create(context);
}
public bool Release(object instance)
{
return true;
}
public void Dispose()
{
}
}
Pretty simple but not enough. Then I need an implementation of IReleasePolicy and I can simply inherit from the default and override a method:
public class LifecycledComponentsReleasePolicy : Castle.MicroKernel.Releasers.LifecycledComponentsReleasePolicy
{
private readonly Type instantiateAndForgetItType = typeof (InstantiateAndForgetIt);
public override void Track(object instance, Burden burden)
{
if (instantiateAndForgetItType.Equals(burden.Model.CustomLifestyle))
{
return;
}
base.Track(instance,burden);
}
}
The last step is the modification of the container’s configuration:
{
var container = new WindsorContainer();
container.Kernel.ReleasePolicy = new LifecycledComponentsReleasePolicy();
container.Register(Component.For<IMySingleton>().ImplementedBy<MySingleton>());
container.Register(Component.For<IMyTransient>().ImplementedBy<MyTransient>().LifeStyle.Custom<InstantiateAndForgetIt>());
return container;
}
Work done!! and bye bye “memory leaks”.
Update: request to have InstantiateAndForgetIt Lifestyle natively supported issue IOC-225 (vote for it)