Search code examples
c#castle-windsor

programmatically change a dependency in Castle Windsor


I have a class that calls out to an internet service to get some data:

public class MarketingService
{
    private IDataProvider _provider;
    public MarketingService(IDataProvider provider)
    {
        _provider = provider;
    }

    public string GetData(int id)
    {
        return _provider.Get(id);
    }
}

Currently I have two providers: HttpDataProvider and FileDataProvider. Normally I will wire up to the HttpDataProvider but if the external web service fails, I'd like to change the system to bind to the FileDataProvider . Something like:

public string GetData(int id)
{
    string result = "";

    try
    {
        result = GetData(id); // call to HttpDataProvider
    }
    catch (Exception)
    {
        // change the Windsor binding so that all future calls go automatically to the
        // FileDataProvier
        // And while I'm at it, retry against the FileDataProvider    
    }

    return result;
}

So when this has been executed all future instances of MarketingService will automatically be wired up to the FileDataProvider. How to change a Windsor binding on the fly?


Solution

  • One solution would be using selector

    public class ForcedImplementationSelector<TService> : IHandlerSelector
    {
        private static Dictionary<Type, Type>  _forcedImplementation = new Dictionary<Type, Type>();
    
        public static void ForceTo<T>() where T: TService
        {
            _forcedImplementation[typeof(TService)] = typeof(T);
        }
    
        public static void ClearForce()
        {
            _forcedImplementation[typeof(TService)] = null;
        }
    
        public bool HasOpinionAbout(string key, Type service)
        {
            return service == typeof (TService);
        }
    
        public IHandler SelectHandler(string key, Type service, IHandler[] handlers)
        {
            var tService = typeof(TService);
            if (_forcedImplementation.ContainsKey(tService) && _forcedImplementation[tService] != null)
            {
                return handlers.FirstOrDefault(handler => handler.ComponentModel.Implementation == _forcedImplementation[tService]);
            }
    
            // return default
            return handlers[0];
        }
    }
    

    Test and usage

    [TestFixture]
    public class Test
    {
        [Test]
        public void ForceImplementation()
        {
            var container = new WindsorContainer();
    
            container.Register(Component.For<IFoo>().ImplementedBy<Foo>());
            container.Register(Component.For<IFoo>().ImplementedBy<Bar>());
    
            container.Kernel.AddHandlerSelector(new ForcedImplementationSelector<IFoo>());
    
            var i = container.Resolve<IFoo>();
            Assert.AreEqual(typeof(Foo), i.GetType());
    
            ForcedImplementationSelector<IFoo>.ForceTo<Bar>();
    
            i = container.Resolve<IFoo>();
            Assert.AreEqual(typeof(Bar), i.GetType());
    
    
            ForcedImplementationSelector<IFoo>.ClearForce();
    
            i = container.Resolve<IFoo>();
            Assert.AreEqual(typeof(Foo), i.GetType());
        }
    }