2013-04-29 3 views
6

У меня есть класс, который взывает к услуге Интернет, чтобы получить некоторые данные:изменить программно зависимость в замок Виндзор

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

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

В настоящее время у меня есть два провайдера: HttpDataProvider и FileDataProvider. Обычно я подключаюсь к HttpDataProvider, но если внешняя веб-служба терпит неудачу, я хотел бы изменить систему для привязки к FileDataProvider. Что-то вроде:

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

Так что, когда это было выполнено все будущие экземпляры MarketingService будут автоматически подключены к FileDataProvider. Кто-нибудь знает, как изменить привязку Виндзора на лету?

ответ

6

Одним из решений будет использовать селектор

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

испытание и использование

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

Мы использовали эту реализацию с успехом, однако наши верхние потребляющие потоки работали со словарем, поэтому мы изменили его на ConcurrentDictionary, чтобы сделать его потокобезопасным, как рекомендовано здесь: https://blogs.msdn.microsoft.com/tess/2009/12/21/высокий процессор-в-сеть-приложения-с помощью A-статического-родового-словаря / – Calum

0

В качестве альтернативы вы можете создать прокси-сервер:

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>()); 

Это будет всегда сначала попытаться получить данные из HttpDataProvider, если не успешно использовать резервную копию. Если вы хотите, вы можете ввести состояние, и после сбоя всегда используйте резервную копию. Таким образом, вы можете продолжать использовать IDataProvider в своем приложении, не требуя получить новый из контейнера.

Смежные вопросы