Search code examples
castle-windsor

Castle Custom Lifestyle per Resolve


With Castle Windsor, let's say I have the following classes:

public class LowLevelComponent
{
}

public class HighLevelComponent
{
    readonly LowLevelComponent LowLevelComponent;

    public HighLevelComponent(LowLevelComponent lowLevelComponent)
    {
        LowLevelComponent = lowLevelComponent;
    }
}

public class ComponentBeingResolved
{
    readonly LowLevelComponent LowLevelComponent;
    readonly HighLevelComponent HighLevelComponent;

    public ComponentBeingResolved(LowLevelComponent lowLevelComponent,
                                  HighLevelComponent highLevelComponent)
    {
        LowLevelComponent = lowLevelComponent;
        HighLevelComponent = highLevelComponent;
    }
}

Registered in the easiest possible way:

container.Register(Component.For<LowLevelComponent>());
container.Register(Component.For<HighLevelComponent>());
container.Register(Component.For<ComponentBeingResolved>());

I'd like to get the same instance of LowLevelComponent used in all dependencies each time I call Resolve.

So, after these calls:

var instance1 = container.Resolve<ComponentBeingResolved>();
var instance2 = container.Resolve<ComponentBeingResolved>();

The following assertions should be true:

instance1.LowLevelComponent == instance1.HighLevelComponent.LowLevelComponent
instance1.LowLevelComponent != instance2.LowLevelComponent
instance1.HighLevelComponent != instance2.HighLevelComponent

I'll also take "you're doing everything wrong, this is what you should do instead" as an answer :-)


Solution

  • Based on Mauricio's link, I got it working using factories:

    public interface IComponentFactory
    {
        T Get<T>();
    }
    
    var container = new WindsorContainer();
    container.AddFacility<TypedFactoryFacility>();
    container.Register(Component.For<LowLevelComponent>()
             .LifeStyle.Custom<ContextualLifestyle>());
    container.Register(Component.For<HighLevelComponent>()
             .LifeStyle.Custom<ContextualLifestyle>());
    container.Register(Component.For<IComponentFactory>().AsFactory());
    //Register the "context-root" component in a child container
    var subContainer = new WindsorContainer();
    subContainer.Register(Component.For<ComponentBeingResolved>()
                .LifeStyle.Transient);
    container.AddChildContainer(subContainer);
    container.Register(
        Component.For<ComponentBeingResolved>()
            .LifeStyle.Transient
            //Here's the magic
            .UsingFactoryMethod(
                () =>
                    {
                        using (new ContainerContext(container))
                            return subContainer.Resolve<ComponentBeingResolved>();
                    }));
    

    Usage:

    var factory = container.Resolve<IComponentFactory>();
    var instance1 = factory.Get<ComponentBeingResolved>();
    var instance2 = factory.Get<ComponentBeingResolved>();
    

    Not sure if this is a good hack or an ugly one, but it works wonderfully.