Search code examples
.netcastle-windsorfactory-pattern

Can the Castle Windsor Factory implied by Type of T in Resolve<T>?


I have the following castle windsor fluent config code ...

container
  .AddFacility<TypedFactoryFacility>()
  .Register(

  Component
    .For<IXxxCache>()
    .ImplementedBy<AppFabricXxxCache>()
    .Named("AppFabricXxxCache")
    .LifeStyle.FromContext(),

  Component
    .For<IXxxCache>()
    .ImplementedBy<DatabaseXxxCache>()
    .Named("DatabaseXxxCache")
    .LifeStyle.FromContext(),

  Component
    .For<IXxxCacheFactory>()
    .ImplementedBy<XxxCacheFactory>()
    .DependsOn(new {cacheName})
  );

The XxxCacheFactory is as follows ...

public class XxxCacheFactory : IXxxCacheFactory
{
    private readonly IWindsorContainer _container;
    private readonly string _cacheName;

    public XxxCacheFactory(IWindsorContainer container, string cacheName)
    {
        _container = container;
        _cacheName = cacheName;
    }

    public IXxxCache Create()
    {
        try
        {
            var cache = new DataCacheFactory().GetCache(_cacheName);
            return _container.Resolve<IXxxCache>("AppFabricXxxCache", new {cache});
        }
        catch (DataCacheException)
        {
            return _container.Resolve<IXxxCache>("DatabaseXxxCache");
        }
    }

    public void Release(IXxxCache component)
    {
        _container.Release(component);
    }
}

I can get this working with the following code ...

[Test]
public void database_xxx_cache_returned_when_cache_does_not_exist()
{
    ConfigurationManager.AppSettings["CacheName"] = "this_cache_does_not_exist";
    var container = Castle.WindsorContainerBootStrap.BootStrapContainerAndRunInstallers<SingletonLifestyleManager>();
    var factory = container.Resolve<IXxxCacheFactory>();
    var cache = factory.Create();
}

Ideally I'd like to cut out the factory creation part and just have the container get the the correct implementation using the factory class I have, like so ...

[Test]
public void database_xxx_cache_returned_when_cache_does_not_exist()
{
    ConfigurationManager.AppSettings["CacheName"] = "this_cache_does_not_exist";
    var container = Castle.WindsorContainerBootStrap.BootStrapContainerAndRunInstallers<SingletonLifestyleManager>();
    var cache = container.Resolve<IXxxCache>();
}

Is this possible? If so, what am I missing?


Solution

  • One of my colleagues pointed me at the factory method support of Windsor

    Just adding this to my installer has got it working ...

    container
        .Register(
            Component
                .For<DataCacheFactory>()
                .ImplementedBy<DataCacheFactory>()
                .LifeStyle.Singleton
            ,
            Component
                .For<IXxxCacheFactory>()
                .ImplementedBy<XxxCacheFactory>()
                .DependsOn(new {cacheName})
                .LifeStyle.Transient
            ,
            Component
                .For<IXxxCache>()
                .UsingFactoryMethod(kernel => kernel.Resolve<IXxxCacheFactory>().Create())
            ,
            Component
                .For<IXxxCache>()
                .ImplementedBy<AppFabricXxxCache>()
                .Named("AppFabricXxxCache")
                .LifeStyle.FromContext()
            ,
            Component
                .For<IXxxCache>()
                .ImplementedBy<DatabaseXxxCache>()
                .Named("DatabaseXxxCache")
                .LifeStyle.FromContext()
         );
    

    I've also used the container to create the DataCacheFactory as that is apparently an expensive operation. So the usage is now ...

    [Test]
    public void database_xxx_cache_returned_when_cache_does_not_exist()
    {
        // ARRANGE
        ConfigurationManager.AppSettings["CacheName"] = "this_cache_does_not_exist";
        var container = Castle.WindsorContainerBootStrap.BootStrapContainerAndRunInstallers<SingletonLifestyleManager>();
    
        // ACT
        var cache = container.Resolve<IXxxCache>();
    
        // ASSERT
        Assert.That(cache, Is.InstanceOf<DatabaseXxxCache>());
    
        // TIDYUP
        container.Dispose();
    }