programmatically change dependency in windsor castle

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

public class MarketingService { private IDataProvider _provider; public MarketingService(IDataProvider provider) { _provider = provider; } public string GetData(int id) { return _provider.Get(id); } } 

I currently have two providers: HttpDataProvider and FileDataProvider. I usually connect to HttpDataProvider, but if the external web service crashes, I would like to change the system to bind to FileDataProvider. Sort of:

 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; } 

Therefore, when this is done, all future instances of MarketingService will be automatically connected to FileDataProvider. How to change Windsor Snap on the fly?

+7
source share
2 answers

One solution will use a 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]; } } 

Testing and use

 [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()); } } 
+7
source

Alternatively, you can create a proxy:

 public class AutoSelectingDataProvider : IDataProvider { public AutoSelectingDataPovider(HttpDataProvider httpDataProvider, FallBackDataProvider fallBackProvider) { _httpDataProvider = httpDataProvider; _fallBackDataProvider = fallBackDataProvider; } public string GetData(int id) { try { return _httpDataProvider.GetData(id); } catch (Exception) { return _fallBackDataProvider.GetData(id); } return result; } } container.Register( Component.For<HttpDataProvider>(), Component.For<FallBackDataProvider>(), Component.For<IDataProvider>().ImplementedBy<FallBackDataProvider>()); 

It will always try to get data from the HttpDataProvider first if the backup fails. If you want, you can enter the state, and after the failure always use the backup. That way, you can continue to use IDataProvider in your application without requiring you to get a new one from the container.

+1
source

Source: https://habr.com/ru/post/943906/


All Articles